<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0">
        <channel>
            <title><![CDATA[David Dal Busco RSS Feed]]></title>
            <description><![CDATA[Freelance Software Engineer]]></description>
            <link>https://daviddalbusco.com/</link>
            <lastBuildDate>Thu, 16 Jul 2026 00:00:00 GMT</lastBuildDate>
            
            
        <item>
          <title><![CDATA[Structuring SvelteKit Load Functions]]></title>
          <description><![CDATA[An architecture for loading data that's isolated, testable, and free of copy-pasted auth logic.]]></description>
          <link>https://daviddalbusco.com/blog/structuring-sveltekit-load-functions/</link>
          <pubDate>Thu, 16 Jul 2026 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/jr-korpa-9XngoIpxcEo-unsplash.jpg" alt="" / loading="lazy"></p>
<blockquote>
<p>Photo by <a href="https://unsplash.com/fr/@jrkorpa?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Jr Korpa</a> on <a href="https://unsplash.com/fr/photos/particules-de-lumiere-rose-neon-et-violette-9XngoIpxcEo?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></p>
</blockquote>
<p>Earlier this year I built a server-side rendered app. I developed it with SvelteKit and dozens of <code>+page.server.ts</code> <a href="https://svelte.dev/docs/kit/load" rel="noopener noreferrer external">load functions</a> that run before pages render. They all needed authentication, and keeping things maintainable quickly became a real challenge.</p>
<p>In this post, I'll walk you through the architecture I ended up creating. To keep the examples concrete, let's pretend we're building an online pizza shop. 🍕</p>
<hr />
<h2>The Problem</h2>
<p>Packing everything into the load function works fine at first:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">// +page.server.ts</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> load</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> PageServerLoad</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">locals</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">depends</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    depends</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">app:auth</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">locals.jwt) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> { pizzas</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2"> };</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> response </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> fetch</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">API_URL</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">/pizzas`</span><span style="color:#F8F8F2">, {</span></span>
<span class="line"><span style="color:#F8F8F2">        headers</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { cookie</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> `jwt=</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">locals.jwt</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2"> }</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">response.ok) {</span></span>
<span class="line"><span style="color:#50FA7B">        error</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">500</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Failed to load pizzas</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> { pizzas</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> response.</span><span style="color:#50FA7B">json</span><span style="color:#F8F8F2">() };</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>As soon as you have ten pages, this falls apart.</p>
<p>The <a href="https://svelte.dev/docs/kit/@sveltejs-kit#LoadEvent" rel="noopener noreferrer external"><code>depends</code></a> call (which tells SvelteKit to re-run the load function when auth state changes), the JWT guard, and the error handling are copy-pasted everywhere.</p>
<p>The fetching logic lives inside the route files, coupling them together and making each one harder to test in isolation.</p>
<p>On top of that, not all requests are SSR. Some happen on the client side and don't need the JWT forwarded as a header, so you end up with two flavors of the same API call with nothing enforcing the difference.</p>
<p>And when you need to change your auth logic or error handling, you likely have to touch every single load file.</p>
<hr />
<h2>The Architecture</h2>
<p>The solution I suggest is a layered structure with clear, distinct responsibilities:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">src</span><span style="color:#FF79C6">/</span></span>
<span class="line"><span style="color:#F8F8F2">  lib</span><span style="color:#FF79C6">/</span></span>
<span class="line"><span style="color:#F8F8F2">    api</span><span style="color:#FF79C6">/</span></span>
<span class="line"><span style="color:#F8F8F2">      _api.ts             ← base </span><span style="color:#FF79C6">class</span><span style="color:#8BE9FD"> for</span><span style="color:#8BE9FD"> all</span><span style="color:#8BE9FD"> API</span><span style="color:#8BE9FD"> calls</span></span>
<span class="line"><span style="color:#8BE9FD">      _api</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD">ssr</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD">ts</span><span style="color:#F8F8F2">         ← </span><span style="color:#8BE9FD">SSR</span><span style="color:#F8F8F2">-</span><span style="color:#8BE9FD">specific</span><span style="color:#8BE9FD"> base</span><span style="color:#F8F8F2"> (</span><span style="color:#8BE9FD">JWT</span><span style="color:#8BE9FD"> forwarding</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#8BE9FD">      pizzas</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD">api</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD">ts</span><span style="color:#F8F8F2">      ← </span><span style="color:#8BE9FD">browser</span><span style="color:#8BE9FD"> API</span></span>
<span class="line"><span style="color:#8BE9FD">      pizzas</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD">api</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD">ssr</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD">ts</span><span style="color:#F8F8F2">  ← </span><span style="color:#8BE9FD">SSR</span><span style="color:#8BE9FD"> API</span></span>
<span class="line"><span style="color:#8BE9FD">    services</span><span style="color:#F8F8F2">/</span></span>
<span class="line"><span style="color:#8BE9FD">      pizzas</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD">services</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD">ts</span></span>
<span class="line"><span style="color:#8BE9FD">  main</span><span style="color:#F8F8F2">/</span></span>
<span class="line"><span style="color:#8BE9FD">    server</span><span style="color:#F8F8F2">/</span></span>
<span class="line"><span style="color:#8BE9FD">      _server</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD">ts</span><span style="color:#F8F8F2">          ← </span><span style="color:#8BE9FD">ServerLoader</span><span style="color:#8BE9FD"> abstract</span><span style="color:#8BE9FD"> base</span><span style="color:#8BE9FD"> class</span></span>
<span class="line"><span style="color:#8BE9FD">      pizzas</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD">server</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD">ts</span></span>
<span class="line"><span style="color:#8BE9FD">  routes</span><span style="color:#F8F8F2">/</span></span>
<span class="line"><span style="color:#8BE9FD">    pizzas</span><span style="color:#F8F8F2">/</span></span>
<span class="line"><span style="color:#F8F8F2">      +</span><span style="color:#8BE9FD">page</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD">server</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD">ts</span><span style="color:#F8F8F2">     ← </span><span style="color:#8BE9FD">one</span><span style="color:#F8F8F2">-</span><span style="color:#8BE9FD">liner</span></span></code></pre><p>The one rule that holds the whole thing together: <strong>route files contain no logic</strong>. They delegate entirely to a server class.</p>
<p>The second: <code>lib</code> <strong>contains nothing SvelteKit-specific</strong>. Keeping it that way makes your business logic agnostic of the presentation layer and potentially allows you to switch your UI, design or even framework without too much pain in the future.</p>
<hr />
<h2>The Foundation: hooks.server.ts</h2>
<p>Our API (and I'm assuming yours too) requires a JWT to authenticate calls. Once issued after authentication, we can rely on <a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#including_credentials" rel="noopener noreferrer external">credentials: 'include'</a> on the client side, which instructs the browser to embed the cookie automatically. However, on the server side, there is no such API and the cookie has to be read manually to pass the JWT along as an HTTP header.</p>
<p>That's why, to make the JWT easily accessible across the application via <a href="https://svelte.dev/docs/kit/hooks#Server-hooks-locals" rel="noopener noreferrer external"><code>locals</code></a> (an object specific to a single request which is available to any route), the first step is setting up a <code>hooks.server.ts</code> that runs every time the SvelteKit server receives a <a href="https://svelte.dev/docs/kit/web-standards#Fetch-APIs-Request" rel="noopener noreferrer external">request</a> and extracts it.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">// hooks.server.ts</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#FF79C6"> type</span><span style="color:#F8F8F2"> { Handle } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@sveltejs/kit</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> handle</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Handle</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">event</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    event.locals.jwt </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> event.cookies.</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">jwt</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#50FA7B"> resolve</span><span style="color:#F8F8F2">(event);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>And the type declaration:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">// app.d.ts</span></span>
<span class="line"><span style="color:#FF79C6">declare</span><span style="color:#F8F8F2"> global {</span></span>
<span class="line"><span style="color:#FF79C6">    namespace</span><span style="color:#8BE9FD;font-style:italic"> App</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        interface</span><span style="color:#8BE9FD;font-style:italic"> Locals</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">            jwt</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h2>Layer 1: The API Split</h2>
<p>All APIs generally share common traits. In this case, they all have an <code>apiUrl</code> and a <code>throwError</code> method to handle exceptions, therefore they can live in a base class.</p>
<blockquote>
<p>Note: I use the <code>_</code> prefix for filename as a convention for package-scoped modules that are not meant to be used directly outside their layer.</p>
</blockquote>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">// lib/api/_api.ts</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> abstract</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> Api</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    protected</span><span style="color:#FF79C6"> readonly</span><span style="color:#F8F8F2"> apiUrl</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    protected</span><span style="color:#FF79C6"> constructor</span><span style="color:#F8F8F2">({ </span><span style="color:#FFB86C;font-style:italic">apiUrl</span><span style="color:#F8F8F2"> }</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { apiUrl</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2"> }) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        this</span><span style="color:#F8F8F2">.apiUrl </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> apiUrl;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    protected</span><span style="color:#FF79C6"> async</span><span style="color:#50FA7B"> throwError</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#FFB86C;font-style:italic">        message</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#FFB86C;font-style:italic">        response</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        response</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Response</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        message</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    })</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">never</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">        switch</span><span style="color:#F8F8F2"> (response.status) {</span></span>
<span class="line"><span style="color:#FF79C6">            case</span><span style="color:#BD93F9"> 400</span><span style="color:#F8F8F2">:</span></span>
<span class="line"><span style="color:#FF79C6">            case</span><span style="color:#BD93F9"> 500</span><span style="color:#F8F8F2">: {</span></span>
<span class="line"><span style="color:#FF79C6">                const</span><span style="color:#F8F8F2"> body </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> response.</span><span style="color:#50FA7B">json</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">catch</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> ({}));</span></span>
<span class="line"><span style="color:#FF79C6">                throw</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> ApiError</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">message</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C"> (</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">response.status</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">): </span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">body.message</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">, {</span></span>
<span class="line"><span style="color:#F8F8F2">                    cause</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> response</span></span>
<span class="line"><span style="color:#F8F8F2">                });</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"><span style="color:#FF79C6">            default</span><span style="color:#F8F8F2">:</span></span>
<span class="line"><span style="color:#FF79C6">                throw</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> ApiError</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">message</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C"> (</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">response.status</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">)`</span><span style="color:#F8F8F2">, { cause</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> response });</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>The <code>throwError</code> method tries to parse the response body as JSON for known error status codes (400, 500) because our API returns a structured error message in those cases. For anything else, it falls back to just the status code.</p>
<p>As for SSR calls, they share one extra trait: the need to pass the JWT as an HTTP header. Another base class, <code>ApiSsr</code>, extends <code>Api</code> and takes the JWT at construction time, exposing a <code>cookieHeader()</code> helper:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">// lib/api/_api.ssr.ts</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> abstract</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> ApiSsr</span><span style="color:#FF79C6"> extends</span><span style="color:#8BE9FD;font-style:italic"> Api</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    readonly</span><span style="color:#F8F8F2"> #jwt</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    protected</span><span style="color:#FF79C6"> constructor</span><span style="color:#F8F8F2">({ </span><span style="color:#FFB86C;font-style:italic">apiUrl</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">jwt</span><span style="color:#F8F8F2"> }</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { apiUrl</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">; jwt</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2"> }) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        super</span><span style="color:#F8F8F2">({ apiUrl });</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        this</span><span style="color:#F8F8F2">.#jwt </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> jwt;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    cookieHeader</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { cookie</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> `jwt=</span><span style="color:#FF79C6">${</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2"> } {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> { cookie</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> `jwt=</span><span style="color:#FF79C6">${</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.#jwt</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2"> };</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><blockquote>
<p>Note: I use <code>.ssr.ts</code> rather than <code>.server.ts</code> - which was my first idea - because it seems that the latter is a globally reserved suffix for SvelteKit.</p>
</blockquote>
<p>With those two base classes in place, each resource gets two API classes: one for the browser, one for SSR.</p>
<p>One might argue that a single class could behave differently depending on where it runs. When I implemented the solution, I felt like the explicit separation was cleaner but, fair enough, I would not be opposed to the alternative.</p>
<p>With the base classes in place, the two API classes serve different purposes. The browser API handles mutations — actions triggered by the user, like creating a pizza:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">// lib/api/pizzas.api.ts</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> ApiPizzas</span><span style="color:#FF79C6"> extends</span><span style="color:#8BE9FD;font-style:italic"> Api</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    create</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">body</span><span style="color:#F8F8F2"> }</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { body</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> CreatePizzaBody</span><span style="color:#F8F8F2"> })</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">CreatePizzaResponse</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> response </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> fetch</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`</span><span style="color:#FF79C6">${</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.apiUrl</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">/pizzas`</span><span style="color:#F8F8F2">, {</span></span>
<span class="line"><span style="color:#F8F8F2">            method</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">POST</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">            credentials</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">include</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">            headers</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#E9F284">                "</span><span style="color:#F1FA8C">Content-Type</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">application/json</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">            },</span></span>
<span class="line"><span style="color:#F8F8F2">            body</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> CreatePizzaBodyCodec.</span><span style="color:#50FA7B">decode</span><span style="color:#F8F8F2">(body)</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">response.ok) {</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#FF79C6"> await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">throwError</span><span style="color:#F8F8F2">({ response, message</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Cannot post /pizzas</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> });</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> result </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> response.</span><span style="color:#50FA7B">json</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> CreatePizzaResponse.</span><span style="color:#50FA7B">parse</span><span style="color:#F8F8F2">(result);</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>The SSR API handles reads — data fetched in load functions before the page renders. As explained, there is no cookie jar on the server, so the JWT has to be forwarded explicitly via the <code>cookieHeader()</code> helper:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">// lib/api/pizzas.api.ssr.ts</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> ApiPizzasSsr</span><span style="color:#FF79C6"> extends</span><span style="color:#8BE9FD;font-style:italic"> ApiSsr</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    list</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">ListPizzasResponse</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> response </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> fetch</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`</span><span style="color:#FF79C6">${</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.apiUrl</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">/pizzas`</span><span style="color:#F8F8F2">, {</span></span>
<span class="line"><span style="color:#F8F8F2">            headers</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">                ...</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">cookieHeader</span><span style="color:#F8F8F2">() </span><span style="color:#6272A4">// → { cookie: `jwt=${this.#jwt}` }</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">response.ok) {</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#FF79C6"> await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">throwError</span><span style="color:#F8F8F2">({ response, message</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Cannot get /pizzas</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> });</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> ApiPizzasSchema.ListPizzasResponse.</span><span style="color:#50FA7B">parse</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">await</span><span style="color:#F8F8F2"> response.</span><span style="color:#50FA7B">json</span><span style="color:#F8F8F2">());</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>In the above snippets you might have noticed the use of <a href="https://zod.dev" rel="noopener noreferrer external">Zod</a> for schema validation. Parsing the response ensures it matches the expected shape at runtime.</p>
<hr />
<h2>Layer 2: Services</h2>
<p>Services sit between the API layer and the page and layout servers. Their role is to act as a proxy that never throws. Instead, they catch errors and return a <code>Result&lt;T&gt;</code>, a discriminated union that should be handled by the caller. I find this pattern - really similar to Rust - more expressive and elegant to deal with potential errors.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">type</span><span style="color:#8BE9FD;font-style:italic"> Result</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> { status</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">success</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">; result</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> T</span><span style="color:#F8F8F2"> } </span><span style="color:#FF79C6">|</span><span style="color:#F8F8F2"> { status</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">error</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">; err</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> unknown</span><span style="color:#F8F8F2"> };</span></span></code></pre><p>When there is additional logic to handle, services become more than a proxy, but the contract stays the same.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">// lib/services/pizzas.services.ts</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> listPizzas</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">args</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { jwt</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2"> })</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Result</span><span style="color:#F8F8F2">&#x3C;{ pizzas</span><span style="color:#FF79C6">:</span><span style="color:#FFB86C;font-style:italic"> Pizza</span><span style="color:#F8F8F2">[] }>> </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> api </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> ApiPizzasSsr.</span><span style="color:#50FA7B">create</span><span style="color:#F8F8F2">(args);</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> { pizzas } </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> api.</span><span style="color:#50FA7B">list</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> { status</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">success</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, result</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { pizzas } };</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> { status</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">error</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, err };</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>A service for fetching a single pizza by ID - e.g. <code>getPizza</code> - would follow the same pattern, taking an additional <code>pizzaId</code> parameter alongside <code>jwt</code>.</p>
<hr />
<h2>Layer 3: The ServerLoader</h2>
<p>As mentioned in the introduction, the goal is to avoid duplicating logic and repetitive code across every load function used by our routes, respectively the layout and page server. <code>ServerLoader</code> is an abstract base class that centralises all of that in one place:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">// main/server/_server.ts</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { error, </span><span style="color:#FF79C6">type</span><span style="color:#F8F8F2"> ServerLoadEvent } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@sveltejs/kit</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> abstract</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> ServerLoader</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    readonly</span><span style="color:#F8F8F2"> #context</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    protected</span><span style="color:#FF79C6"> constructor</span><span style="color:#F8F8F2">({ </span><span style="color:#FFB86C;font-style:italic">context</span><span style="color:#F8F8F2"> }</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { context</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2"> }) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        this</span><span style="color:#F8F8F2">.#context </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> context;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    protected</span><span style="color:#FF79C6"> async</span><span style="color:#50FA7B"> _load</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">>({</span></span>
<span class="line"><span style="color:#F8F8F2">        $event: { </span><span style="color:#FFB86C;font-style:italic">depends</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">locals</span><span style="color:#F8F8F2"> },</span></span>
<span class="line"><span style="color:#FFB86C;font-style:italic">        errorFallbackMsg</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#FFB86C;font-style:italic">        loaderFn</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        $event</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> ServerLoadEvent</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        errorFallbackMsg</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#50FA7B">        loaderFn</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">params</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { jwt</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Result</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">>>;</span></span>
<span class="line"><span style="color:#F8F8F2">    })</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#50FA7B">        depends</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">main:auth</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (</span><span style="color:#50FA7B">isNullish</span><span style="color:#F8F8F2">(locals.jwt)) {</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> result </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> loaderFn</span><span style="color:#F8F8F2">({ jwt</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> locals.jwt });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (result.status </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">error</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">            console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`[</span><span style="color:#FF79C6">${</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.#context</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">]`</span><span style="color:#F8F8F2">, result.err);</span></span>
<span class="line"><span style="color:#50FA7B">            error</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">500</span><span style="color:#F8F8F2">, result.err </span><span style="color:#FF79C6">instanceof</span><span style="color:#8BE9FD;font-style:italic"> Error</span><span style="color:#FF79C6"> ?</span><span style="color:#F8F8F2"> result.err.message </span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> errorFallbackMsg);</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> result.result;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    protected</span><span style="color:#FF79C6"> async</span><span style="color:#50FA7B"> _loadWithSlug</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">>({</span></span>
<span class="line"><span style="color:#F8F8F2">        $event: { </span><span style="color:#FFB86C;font-style:italic">params</span><span style="color:#F8F8F2">, </span><span style="color:#FF79C6">...</span><span style="color:#FFB86C;font-style:italic">eventRest</span><span style="color:#F8F8F2"> },</span></span>
<span class="line"><span style="color:#F8F8F2">        errorFallbackMsgs: { load: </span><span style="color:#FFB86C;font-style:italic">errorFallbackMsg</span><span style="color:#F8F8F2">, missingSlug: </span><span style="color:#FFB86C;font-style:italic">errorMissingSlug</span><span style="color:#F8F8F2"> },</span></span>
<span class="line"><span style="color:#FFB86C;font-style:italic">        loaderWithSlugFn</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        $event</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> ServerLoadEvent</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        errorFallbackMsgs</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { load</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">; missingSlug</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2"> };</span></span>
<span class="line"><span style="color:#50FA7B">        loaderWithSlugFn</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">params</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { jwt</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">; slug</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Result</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">>>;</span></span>
<span class="line"><span style="color:#F8F8F2">    })</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> { slug } </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> params;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (</span><span style="color:#50FA7B">isEmptyString</span><span style="color:#F8F8F2">(slug)) {</span></span>
<span class="line"><span style="color:#50FA7B">            error</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">404</span><span style="color:#F8F8F2">, errorMissingSlug);</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#50FA7B"> loaderFn</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">args</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { jwt</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> loaderWithSlugFn</span><span style="color:#F8F8F2">({ slug, </span><span style="color:#FF79C6">...</span><span style="color:#F8F8F2">args });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#FF79C6"> await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">_load</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">            $event</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { params, </span><span style="color:#FF79C6">...</span><span style="color:#F8F8F2">eventRest },</span></span>
<span class="line"><span style="color:#F8F8F2">            loaderFn,</span></span>
<span class="line"><span style="color:#F8F8F2">            errorFallbackMsg</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>The class exposes two protected methods. <code>_load</code> is the general one, meant to be called with a loader function that hooks into the service layer. <code>_loadWithSlug</code> is for dynamic routes where a slug is expected in the URL params.</p>
<p>On error, both rely on SvelteKit's <a href="https://svelte.dev/docs/kit/@sveltejs-kit#error" rel="noopener noreferrer external"><code>error()</code></a> function — a function that never returns — to throw a 404 or 500 with a meaningful message.</p>
<p>Since the application has a sign-in and sign-out flow, every load registers a <code>depends('main:auth')</code> dependency. This tells SvelteKit to re-run the load function whenever <code>invalidate('main:auth')</code> is called, for example after a successful login or logout.</p>
<p>Lastly, also related to authentication, the JWT guard returns <code>undefined</code> early if <code>locals.jwt</code> is not set. The UI is responsible for handling the unauthenticated state.</p>
<hr />
<h2>Layer 4: Server Classes</h2>
<p>Server classes are the concrete classes that the route files will instantiate. They extend <code>ServerLoader</code>, pass a <code>context</code> string for log tagging, and expose typed public methods:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">// main/server/pizzas.server.ts</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#FF79C6"> type</span><span style="color:#F8F8F2"> { ServerLoadEvent } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@sveltejs/kit</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { listPizzas } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">$lib/services/pizzas.services</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> PizzasServer</span><span style="color:#FF79C6"> extends</span><span style="color:#8BE9FD;font-style:italic"> ServerLoader</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    private</span><span style="color:#FF79C6"> constructor</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">init</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { context</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2"> }) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        super</span><span style="color:#F8F8F2">(init);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    static</span><span style="color:#50FA7B"> create</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> PizzasServer</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> PizzasServer</span><span style="color:#F8F8F2">({ context</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">pizzas</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> });</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    async</span><span style="color:#50FA7B"> listPizzas</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">$event</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> ServerLoadEvent</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;{ pizzas</span><span style="color:#FF79C6">:</span><span style="color:#FFB86C;font-style:italic"> Pizza</span><span style="color:#F8F8F2">[] } </span><span style="color:#FF79C6">|</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#FF79C6"> await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">_load</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">            $event,</span></span>
<span class="line"><span style="color:#F8F8F2">            errorFallbackMsg</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Unexpected error while loading the pizzas</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">            loaderFn</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> listPizzas</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>For slug-based routes:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">// main/server/pizza.server.ts</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#FF79C6"> type</span><span style="color:#F8F8F2"> { ServerLoadEvent } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@sveltejs/kit</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { getPizza } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">$lib/services/pizza.services</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> PizzaServer</span><span style="color:#FF79C6"> extends</span><span style="color:#8BE9FD;font-style:italic"> ServerLoader</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    private</span><span style="color:#FF79C6"> constructor</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">init</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { context</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2"> }) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        super</span><span style="color:#F8F8F2">(init);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    static</span><span style="color:#50FA7B"> create</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> PizzaServer</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> PizzaServer</span><span style="color:#F8F8F2">({ context</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">pizza</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> });</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    async</span><span style="color:#50FA7B"> getPizza</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">$event</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> ServerLoadEvent</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;{ pizza</span><span style="color:#FF79C6">:</span><span style="color:#FFB86C;font-style:italic"> Pizza</span><span style="color:#F8F8F2"> } </span><span style="color:#FF79C6">|</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#FF79C6"> await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">_loadWithSlug</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">            $event,</span></span>
<span class="line"><span style="color:#F8F8F2">            errorFallbackMsgs</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">                load</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Unexpected error while loading the pizza</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">                missingSlug</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Pizza cannot be fetched, no pizza ID provided.</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">            },</span></span>
<span class="line"><span style="color:#50FA7B">            loaderWithSlugFn</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ({ slug: </span><span style="color:#FFB86C;font-style:italic">pizzaId</span><span style="color:#F8F8F2">, </span><span style="color:#FF79C6">...</span><span style="color:#FFB86C;font-style:italic">rest</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> getPizza</span><span style="color:#F8F8F2">({ pizzaId, </span><span style="color:#FF79C6">...</span><span style="color:#F8F8F2">rest })</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h2>Layer 5: Route Files</h2>
<p>With all the logic extracted, route files become one-liners:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">// routes/pizzas/+page.server.ts</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#FF79C6"> type</span><span style="color:#F8F8F2"> { PageServerLoad } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./$types</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> load</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> PageServerLoad</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">$event</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> PizzasServer.</span><span style="color:#50FA7B">create</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">listPizzas</span><span style="color:#F8F8F2">($event);</span></span></code></pre><pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">// routes/pizzas/[slug]/+page.server.ts</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#FF79C6"> type</span><span style="color:#F8F8F2"> { PageServerLoad } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./$types</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> load</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> PageServerLoad</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">$event</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> PizzaServer.</span><span style="color:#50FA7B">create</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">getPizza</span><span style="color:#F8F8F2">($event);</span></span></code></pre><p>With this architecture, a route file is just a declaration of intent.</p>
<blockquote>
<p>Note that the same pattern works similarly for <code>LayoutServerLoad</code>.</p>
</blockquote>
<hr />
<h2>Testability</h2>
<p>Because each layer has no dependency on the others, testing is straightforward. The API layer, services, and server classes can all be tested independently by mocking <code>fetch</code> globally. The route file test is just a spy confirming delegation to the server class.</p>
<hr />
<h2>Summary</h2>
<table>
<thead>
<tr><th>Layer</th><th>Responsibility</th><th>SvelteKit imports?</th></tr>
</thead>
<tbody>
<tr><td><code>_api.ts</code></td><td>HTTP, error throwing</td><td>No</td></tr>
<tr><td><code>_api.ssr.ts</code></td><td>JWT cookie forwarding</td><td>No</td></tr>
<tr><td><code>pizzas.api.ts</code></td><td>Browser mutations (<code>credentials: 'include'</code>)</td><td>No</td></tr>
<tr><td><code>pizzas.api.ssr.ts</code></td><td>SSR reads (JWT header)</td><td>No</td></tr>
<tr><td>Services</td><td>Proxy API calls, return <code>Result&lt;T&gt;</code></td><td>No</td></tr>
<tr><td><code>_server.ts</code></td><td>Auth guard, <code>depends</code>, error handling</td><td>Yes</td></tr>
<tr><td>Server classes</td><td>Concrete load methods</td><td>Yes</td></tr>
<tr><td>Route files</td><td>One-line delegation</td><td>Yes</td></tr>
</tbody>
</table>
<hr />
<h2>Conclusion</h2>
<p>With this architecture, layers are clearly separated and logic isn't duplicated. Adding a new route means writing one new method and one new line in a route file, which is about as boring as it should be even for your LLM.</p>
<p>Until next time!<br />
David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Seedorf UR - Hinter Jochli - Dallenwil]]></title>
          <link>https://daviddalbusco.com/trails/2026/seedorf-ur-hinter-jochli-dallenwil/</link>
          <pubDate>Fri, 10 Jul 2026 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>I did exactly the same trail one year ago, still one of the most pleasant trails in central Switzerland. That said, 31°C is a bit too warm for running 🥵, so I went up at a slower pace this time.</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Building a Trails Section With Maps]]></title>
          <description><![CDATA[Turns out 'just add a map' is a bigger question than it sounds.]]></description>
          <link>https://daviddalbusco.com/blog/building-a-trails-section-with-maps/</link>
          <pubDate>Thu, 09 Jul 2026 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/nenad-novakovic-mHX5e1gFQVo-unsplash.jpg" alt="" / loading="lazy"></p>
<blockquote>
<p>Photo by <a href="https://unsplash.com/fr/@nnx0r?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Nenad Novaković</a> on <a href="https://unsplash.com/fr/photos/un-fond-bleu-et-vert-avec-des-lignes-ondulees-mHX5e1gFQVo?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></p>
</blockquote>
<p>I've been running the Alps for a few years now. At some point I wanted my own record of it, not just a Strava feed, but something on my own site. So I started building a <a href="/trails" rel="noopener noreferrer external">Trails</a> section with the GPX tracks and photos of my &quot;adventures&quot;.</p>
<p>Building it was also a good excuse to take a look at the map landscape nowadays, and it turned out that &quot;just add a map&quot; was a bit of a bigger question than it sounds. So, I developed it like this.</p>
<hr />
<h2>Finding a map</h2>
<p>Whatever library you pick to render a map, Leaflet, MapLibre, or anything else, it draws what you give it. You need your data, but you also need a source of tiles (raster images or vector geometry) to actually see a map, and that's the part that turns &quot;add a map&quot; into a real decision.</p>
<h3>The options</h3>
<p>I went looking, and from what I understood, those are the options I had to gather the tiles:</p>
<h4>Self-hosting</h4>
<p>Since I already self-host my website, my analytics, and other tools, checking whether I could self-host the map tiles too was my first instinct. I found <a href="https://protomaps.com/" rel="noopener noreferrer external">Protomaps</a>, which publishes pre-built vector tile archives you can just serve as static files. Minimal setup, exactly my kind of thing. However, looking at the actual rendering, it wasn't quite the look I wanted. Since I'm displaying trails in the mountains, I wanted some relief, not a flat design.</p>
<p>I then came across a project called <a href="https://mapterhorn.com" rel="noopener noreferrer external">Mapterhorn</a>, mentioned in Protomaps' <a href="https://protomaps.com/blog/mapterhorn-terrain/" rel="noopener noreferrer external">blog</a>, meant to provide exactly that kind of elevation data, but I wasn't exactly sure how to put all the pieces together, nor whether it was still in development or production ready. It would have been cool to use, as it seems to be developed in Zurich.</p>
<p>I also looked at serving - and generating - my own tiles with <a href="https://github.com/onthegomap/planetiler" rel="noopener noreferrer external">Planetiler</a> plus Martin or TileServer GL, but even if someone more comfortable with DevOps would say it's not that much work, it felt like more overhead and maintenance than I wanted to take on for a personal site.</p>
<p>Long story short, I got lost in limbo.</p>
<h4>Regional data</h4>
<p>Since I mostly run in Switzerland, I then checked if <a href="https://www.swisstopo.admin.ch" rel="noopener noreferrer external">swisstopo</a>, the national mapping agency, had an API.</p>
<p>Spoiler alert: they do, and it's a really interesting <a href="https://www.geo.admin.ch/fr/interface-de-programmation-api" rel="noopener noreferrer external">service</a>. They serve their own tiles, satellite imagery included, for free, no signup, no API key, just a fair-use policy.</p>
<p>I was tempted, but since I also wanted to show off (brag 😅) the mountains I ran when I visited Japan, that ruled it out.</p>
<h4>Commercial providers</h4>
<p>Ultimately I opened up to the idea of using a third-party cloud provider, <a href="https://www.maptiler.com/" rel="noopener noreferrer external">MapTiler</a> (was pleased to learn they're also based in Switzerland, at least part of it, it seems) and <a href="https://carto.com" rel="noopener noreferrer external">Carto</a> looked like solid options. Both work as a drop-in source for Leaflet or MapLibre, and have a free tier. MapTiler even ships an &quot;Outdoor&quot; style with trails built in, exactly the look I had in mind.</p>
<p>The catch is that free tiers come with quotas, though generous enough for a website like mine. Carto even seems to have an unlimited free tier, but to be honest with you, their tagline &quot;The Agentic GIS Platform&quot; just ruled them out.</p>
<p>There is also <a href="https://www.mapbox.com/" rel="noopener noreferrer external">Mapbox</a>, which felt too commercial and <a href="https://mapsplatform.google.com/lp/maps-apis/" rel="noopener noreferrer external">Google Maps API</a> was not even an option.</p>
<p>While browsing further, I noticed that Wikiloc actually displayed an &quot;Apple&quot; footer in their maps. I told myself: wait, Apple is also a map provider?</p>
<h3>What I picked</h3>
<p>I ended up going with the last option. Apple has its own closed map renderer, exposed as a JS library for the web, and they'd just released a <a href="https://webkit.org/blog/18027/discover-mapkit-js-6-rebuilt-for-todays-web-developer/" rel="noopener noreferrer external">newest version</a>. If you already have an Apple Developer Program membership ($99/year), it's &quot;free&quot; to use, with a daily quota of 250,000 map views and 25,000 service calls.</p>
<p>That's my case, since I publish a few app updates to their store each year, and it always felt like a bit of a scam to pay for it every year as it does not seem worth the money. So I figured, why not get some value back out of it by actually using it for something. Plus, their free tier should easily cover the little traffic of my website.</p>
<hr />
<h2>Building it</h2>
<p>Here's how I actually wired it into the <a href="/trails" rel="noopener noreferrer external">Trails</a> page.</p>
<h3>Getting started</h3>
<p>To show anything on a map, you need points. In my case, those come from parsing the GPX data of each trail. I'll skip that part here for brevity, but each point ends up looking like this: a coordinate (latitude and longitude) and a unique id.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">interface</span><span style="color:#8BE9FD;font-style:italic"> MapGpxPoint</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    id</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    lat</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    lon</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>The id is optional but useful when you want to manipulate the points on the UI side, for example when hovering a point on the map.</p>
<h3>Loading the map</h3>
<p>There's an official open source <a href="https://github.com/apple/mapkit-loader" rel="noopener noreferrer external">mapkit-loader</a> to bring the library in, though I have to say that even though I used it, I'm not fully sold on it.</p>
<p>The loader injects a script tag into the page. On one hand, I would have preferred an <code>await import()</code> API, arguably a matter of taste, but on the other, injecting a script tag at runtime within my app is never something I'm really a fan of (#paranoid) and could raise some questions around handling the Content-Security-Policy (the library does support a nonce, though it's not the most elegant solution).</p>
<p>It also seems to load the entire MapKit core regardless of what you actually use, which makes me wonder if that couldn't be trimmed down for performance.</p>
<p>Lastly, you end up holding two separate entities in your state if you want to manipulate the map once it's rendered: the MapKit API itself, and the interactive map instance. I would have expected a single reference.</p>
<p>Anyway, I could have rewritten the loader but couldn't really influence the rest, so I just went with it.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { load, </span><span style="color:#FF79C6">type</span><span style="color:#FFB86C;font-style:italic"> MapKit</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> AppleMapKit, </span><span style="color:#FF79C6">type</span><span style="color:#F8F8F2"> Map } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@apple/mapkit-loader</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> interface</span><span style="color:#8BE9FD;font-style:italic"> MapKit</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    mapkit</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> AppleMapKit</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    map</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Map</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> loadMap</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">anchor</span><span style="color:#F8F8F2"> }</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { anchor</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLElement</span><span style="color:#F8F8F2"> })</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">MapKit</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> mapkit </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> load</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        libraries</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">map</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">annotations</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">overlays</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">        token</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">YOUR_TOKEN</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> map </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#F8F8F2"> mapkit.</span><span style="color:#50FA7B">Map</span><span style="color:#F8F8F2">(anchor);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        mapkit,</span></span>
<span class="line"><span style="color:#F8F8F2">        map</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Loading the map requires a token, which you can create in your Apple Developer Dashboard and which can be scoped to a specific domain. For now, we just keep it simple and hardcoded, I'll come back to how I actually handle it in production later on in this blog post.</p>
<p>The <code>libraries</code> are the <a href="https://developer.apple.com/documentation/mapkitjs/loading-the-latest-version-of-mapkit-js#Select-MapKit-JS-libraries" rel="noopener noreferrer external">list of services</a> you need for your map. Since all I'm doing is showing a map, a route, and a couple of markers, <code>map</code>, <code>annotations</code>, and <code>overlays</code> cover it.</p>
<h3>Displaying markers</h3>
<p>Each trail on the site needs a starting point marked on the map. A visual hint of where the trail begins, but also something a visitor can click to actually open the trail page from the map itself.</p>
<p><img src="https://daviddalbusco.com/assets/images/building-a-trails-section-map.png" alt="A screenshot of the website trails map with markers" / loading="lazy"></p>
<p>A marker is a <code>mapkit.MarkerAnnotation</code>, adding an annotation is what gets a point displayed. The popover, the link, is resolved with a custom callout shown in a bubble:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> markers </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> annotations.</span><span style="color:#50FA7B">map</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#F8F8F2">    ({ location: { </span><span style="color:#FFB86C;font-style:italic">lat</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">lon</span><span style="color:#F8F8F2"> }, </span><span style="color:#FFB86C;font-style:italic">title</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">pathname</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span></span>
<span class="line"><span style="color:#FF79C6;font-weight:bold">        new</span><span style="color:#F8F8F2"> mapkit.</span><span style="color:#50FA7B">MarkerAnnotation</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6;font-weight:bold">new</span><span style="color:#F8F8F2"> mapkit.</span><span style="color:#50FA7B">Coordinate</span><span style="color:#F8F8F2">(lat, lon), {</span></span>
<span class="line"><span style="color:#F8F8F2">            title,</span></span>
<span class="line"><span style="color:#F8F8F2">            callout</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">                calloutContentForAnnotation</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">                    const</span><span style="color:#F8F8F2"> link </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> document.</span><span style="color:#50FA7B">createElement</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">a</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">                    link.href </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> pathname;</span></span>
<span class="line"><span style="color:#F8F8F2">                    link.textContent </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> title;</span></span>
<span class="line"><span style="color:#F8F8F2">                    link.</span><span style="color:#50FA7B">setAttribute</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">aria-label</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#F1FA8C">`View </span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">title</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">                    return</span><span style="color:#F8F8F2"> link;</span></span>
<span class="line"><span style="color:#F8F8F2">                }</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"><span style="color:#F8F8F2">        })</span></span>
<span class="line"><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">map.</span><span style="color:#50FA7B">addAnnotations</span><span style="color:#F8F8F2">(markers);</span></span></code></pre><p>An annotation is really just a GPS point extended with a bit of metadata, a title, and a link (<code>pathname</code>) to navigate to when clicked:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">interface</span><span style="color:#8BE9FD;font-style:italic"> MapAnnotation</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    location</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> MapGpxPoint</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    title</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    pathname</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>There's more to configure than that, colors, custom glyphs, visibility of the title, and so on.</p>
<h3>Drawing the route</h3>
<p>Displaying a Trails section has two components: a list and a details page. While showing all the trails with markers could be resolved by the previous chapter, on the details page I wanted to go a step further by drawing the route, the actual trace I followed when I ran or biked.</p>
<p><img src="https://daviddalbusco.com/assets/images/building-a-trails-section-route.png" alt="A screenshot of the website trail detail map with its route" / loading="lazy"></p>
<p>This can be resolved by mapping all the points of a track to coordinates and joining those through an overlay.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> coordinates </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> points.</span><span style="color:#50FA7B">map</span><span style="color:#F8F8F2">(({ </span><span style="color:#FFB86C;font-style:italic">lat</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">lon</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#F8F8F2"> mapkit.</span><span style="color:#50FA7B">Coordinate</span><span style="color:#F8F8F2">(lat, lon));</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> routeOverlay </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#F8F8F2"> mapkit.</span><span style="color:#50FA7B">PolylineOverlay</span><span style="color:#F8F8F2">(coordinates, {</span></span>
<span class="line"><span style="color:#F8F8F2">    style</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#F8F8F2"> mapkit.</span><span style="color:#50FA7B">Style</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        lineWidth</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 4</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        strokeColor</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">#ff65a9</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        lineJoin</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">round</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        lineCap</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">round</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    })</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">map.</span><span style="color:#50FA7B">addOverlay</span><span style="color:#F8F8F2">(routeOverlay);</span></span></code></pre><p>Once both the markers and the route are added, <code>map.showItems([...markers, routeOverlay])</code> adjusts the camera to fit everything, no manual center/zoom math needed.</p>
<h3>Keeping everything in frame</h3>
<p>Once all the points and the route are loaded, I show everything on screen. If I added a point in Switzerland and one in Japan, I want the map to show both, not be centered in South America ;)</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> focusItems</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (kit </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { map } </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> kit;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> items </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> [</span><span style="color:#FF79C6">...</span><span style="color:#F8F8F2">(map.annotations </span><span style="color:#FF79C6">??</span><span style="color:#F8F8F2"> []), </span><span style="color:#FF79C6">...</span><span style="color:#F8F8F2">(map.overlays </span><span style="color:#FF79C6">??</span><span style="color:#F8F8F2"> [])];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (items.length </span><span style="color:#FF79C6">></span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">        map.</span><span style="color:#50FA7B">showItems</span><span style="color:#F8F8F2">(items);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Worth noting my site doesn't load anything in the maps dynamically at runtime, so there's no UX quirk where the map would recenter while the user isn't expecting it.</p>
<h3>Syncing a point</h3>
<p>On the details page, I also render an elevation chart (maybe a topic for another blog post). Similarly to what Wikiloc and Strava do, I wanted the map to show the point corresponding to the elevation the user is hovering over on the chart.</p>
<p><img src="https://daviddalbusco.com/assets/images/building-a-trails-section-hover.png" alt="A screenshot of the chart hovered with the related map point displayed" / loading="lazy"></p>
<p>This can be achieved by getting some metadata through a hover callback on the chart, most charting libraries support this, I used <a href="https://www.layerchart.com/" rel="noopener noreferrer external">LayerChart</a>. In this case, the metadata is the unique id of the point, and I update the map whenever the value changes.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">// Not a state on purpose. Used for logic, not rendering.</span></span>
<span class="line"><span style="color:#FF79C6">let</span><span style="color:#F8F8F2"> selectedPoint</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> MarkerAnnotation</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#FF79C6"> =</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">$effect</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (selectedPointId </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (selectedPoint </span><span style="color:#FF79C6">!==</span><span style="color:#BD93F9"> undefined</span><span style="color:#FF79C6"> &#x26;&#x26;</span><span style="color:#F8F8F2"> selectedPoint </span><span style="color:#FF79C6">!==</span><span style="color:#BD93F9"> null</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">            map.</span><span style="color:#50FA7B">removeAnnotation</span><span style="color:#F8F8F2">(selectedPoint);</span></span>
<span class="line"><span style="color:#F8F8F2">            selectedPoint </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> null</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> point </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> points.</span><span style="color:#50FA7B">find</span><span style="color:#F8F8F2">(({ </span><span style="color:#FFB86C;font-style:italic">id</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> id </span><span style="color:#FF79C6">===</span><span style="color:#F8F8F2"> selectedPointId);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (point </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> coordinate </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#F8F8F2"> mapkit.</span><span style="color:#50FA7B">Coordinate</span><span style="color:#F8F8F2">(point.lat, point.lon);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (selectedPoint </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> undefined</span><span style="color:#FF79C6"> ||</span><span style="color:#F8F8F2"> selectedPoint </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> null</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">        selectedPoint </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#F8F8F2"> mapkit.</span><span style="color:#50FA7B">MarkerAnnotation</span><span style="color:#F8F8F2">(coordinate, {</span></span>
<span class="line"><span style="color:#F8F8F2">            color</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">#ff65a9</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">            glyphText</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">●</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"><span style="color:#F8F8F2">        map.</span><span style="color:#50FA7B">addAnnotation</span><span style="color:#F8F8F2">(selectedPoint);</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    selectedPoint.coordinate </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> coordinate;</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><p>In a first approach, I removed and recreated the marker every time the selected point changed, but that turned out to be laggy, what a surprise 😂. I figured out I could just keep a reference to the rendered annotation and update its coordinate instead, which moves the point fast enough.</p>
<h3>Putting it all together</h3>
<p>That's basically it for rendering a map with some points and routes plus some real time interaction. You'd likely need to tweak it for your needs, but I hope it gives you a quick start.</p>
<p>For the Svelte lovers, here's the <a href="https://github.com/peterpeterparker/daviddalbusco.com/blob/main/src/lib/trails/components/Map.svelte" rel="noopener noreferrer external">full implementation</a>. Not that proud of it, I find it a bit overbloated. If this weren't my website and some more professional work, I would like to split the effects to have better control, but yeah, if it works, it works 😄.</p>
<hr />
<h2>Going to production</h2>
<p>I mentioned earlier that loading the map requires a token, and that it can be scoped to a specific domain. So when I was ready to go live, that's exactly what I did, generated a real token. Or rather, tokens, plural, since I actually needed two: my site is reachable both on <a href="https://daviddalbusco.com" rel="noopener noreferrer external">daviddalbusco.com</a> and <a href="https://www.daviddalbusco.com" rel="noopener noreferrer external">www.daviddalbusco.com</a>, and Apple only lets you scope a token to one domain at a time, not both at once.</p>
<p>Once I created those two tokens, I had to integrate them into my website. Since it is prerendered, the simplest approach would have been to use two environment variables and drop the tokens in a <code>.env</code> file. Even if the repo is public, that's not really a big deal, the tokens end up on the frontend anyway, on the client side, and are de facto public regardless.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2"># .env</span></span>
<span class="line"><span style="color:#F8F8F2">PUBLIC_MAPKIT_TOKEN_ROOT</span><span style="color:#FF79C6">=...</span></span>
<span class="line"><span style="color:#F8F8F2">PUBLIC_MAPKIT_TOKEN_WWW</span><span style="color:#FF79C6">=...</span></span></code></pre><p>Still, it felt nicer, or cleaner, to keep them out of the repo. Instead, I could have used GitHub Actions secrets and referenced them in the build workflow:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">-</span><span style="color:#8BE9FD"> name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> Build</span></span>
<span class="line"><span style="color:#8BE9FD">  run</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> npm run build</span></span>
<span class="line"><span style="color:#8BE9FD">  env</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">    PUBLIC_MAPKIT_TOKEN_ROOT</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> ${{ secrets.MAPKIT_TOKEN_ROOT }}</span></span>
<span class="line"><span style="color:#8BE9FD">    PUBLIC_MAPKIT_TOKEN_WWW</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> ${{ secrets.MAPKIT_TOKEN_WWW }}</span></span></code></pre><p>I was about to do exactly that, but then I thought, why not build something a bit more fun instead 🤪.</p>
<p>Since I self-host the site with <a href="https://kyushu.dev" rel="noopener noreferrer external">Kyushu</a>, I have a backend worker that serves the assets on the web, but it can do more than that. The same function can also act as an API and supports env vars. So why not extend it to also serve the token, by adding a <code>/api/mapkit/token</code> route that responds to <code>GET</code> requests 💡:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    async</span><span style="color:#50FA7B"> fetch</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">request</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">env</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> { pathname } </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> URL</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">parse</span><span style="color:#F8F8F2">(request.url) </span><span style="color:#FF79C6">??</span><span style="color:#F8F8F2"> { pathname</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2"> };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (request.method </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">GET</span><span style="color:#E9F284">"</span><span style="color:#FF79C6"> &#x26;&#x26;</span><span style="color:#F8F8F2"> pathname </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">/api/mapkit/token</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#F8F8F2">; </span><span style="color:#6272A4">// TODO returning the token</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> fetchAsset</span><span style="color:#F8F8F2">(request, env);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">} </span><span style="color:#FF79C6">satisfies</span><span style="color:#8BE9FD;font-style:italic"> ExportedHandler</span><span style="color:#F8F8F2">;</span></span></code></pre><p>Returning the token is relatively straightforward, it's basically just about identifying the <code>GET</code> request and providing back an adequate JSON body, but there were two tricks I had to work out.</p>
<p>Given there are two tokens, one for the root domain and one for www, the function needs to know which domain it's actually being called from, since there's just one function serving both. Locally, reading the <code>Host</code> header would have been enough, but in production I run Caddy as a reverse proxy in front of it, and that value no longer matches. Fortunately, I found out I could use <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Host" rel="noopener noreferrer external">x-forwarded-host</a>, the header that carries the original host that was requested.</p>
<p>Aside from this, my first implementation didn't work either, because I declared the record holding the environment variables globally. Since Kyushu freezes the worker's memory and only evaluates variables at runtime, those ended up undefined. That's why I had to move the reading of those variables inside the function itself, so they're evaluated at request time.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> resolveHost</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">request</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> WorkerRequest</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> =></span></span>
<span class="line"><span style="color:#F8F8F2">    request.headers?.[</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">x-forwarded-host</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">] </span><span style="color:#FF79C6">??</span><span style="color:#F8F8F2"> request.headers?.host;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> fetchMapkitToken</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> ExportedHandler</span><span style="color:#F8F8F2">[</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">fetch</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">] </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">request</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> host </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> resolveHost</span><span style="color:#F8F8F2">(request);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (host </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">            status</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 405</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">            body</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Method Not Allowed</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">        };</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> MAPKIT_TOKENS</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Record</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#E9F284">        "</span><span style="color:#F1FA8C">daviddalbusco.com</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> process.env.</span><span style="color:#BD93F9">MAPKIT_TOKEN_ROOT</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">        "</span><span style="color:#F1FA8C">www.daviddalbusco.com</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> process.env.</span><span style="color:#BD93F9">MAPKIT_TOKEN_WWW</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> token </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> MAPKIT_TOKENS[host];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (token </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">            status</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 403</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">            body</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Forbidden</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">        };</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        status</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 200</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        headers</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#E9F284">            "</span><span style="color:#F1FA8C">content-type</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">application/json</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">            "</span><span style="color:#F1FA8C">cache-control</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">no-store</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">        },</span></span>
<span class="line"><span style="color:#F8F8F2">        body</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">stringify</span><span style="color:#F8F8F2">({ token })</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Lastly, on the client side, I modified the loading of the map to pre-fetch the token first before using it to load the map (instead of the plain hardcoded value from the earlier chapter):</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> interface</span><span style="color:#8BE9FD;font-style:italic"> MapKit</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    mapkit</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> AppleMapKit</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    map</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Map</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> loadMap</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">args</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { anchor</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLElement</span><span style="color:#F8F8F2"> })</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Result</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">MapKit</span><span style="color:#F8F8F2">>> </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> safeExec</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> token </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> loadToken</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> loadMapKit</span><span style="color:#F8F8F2">({ </span><span style="color:#FF79C6">...</span><span style="color:#F8F8F2">args, </span><span style="color:#FF79C6">...</span><span style="color:#F8F8F2">token });</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> loadMapKit</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ({</span></span>
<span class="line"><span style="color:#F8F8F2">    anchor,</span></span>
<span class="line"><span style="color:#F8F8F2">    token</span></span>
<span class="line"><span style="color:#F8F8F2">}</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    anchor</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLElement</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    token</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">})</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">MapKit</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> mapkit </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> load</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        libraries</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">map</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">annotations</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">overlays</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">        token</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> map </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#F8F8F2"> mapkit.</span><span style="color:#50FA7B">Map</span><span style="color:#F8F8F2">(anchor);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        mapkit,</span></span>
<span class="line"><span style="color:#F8F8F2">        map</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> loadToken</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;{ token</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2"> }> </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> response </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> fetch</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/api/mapkit/token</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">response.ok) {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> text </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> response.</span><span style="color:#50FA7B">text</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">        throw</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Error</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">response.status</span><span style="color:#FF79C6">}</span><span style="color:#FF79C6"> ${</span><span style="color:#F8F8F2">text</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> response.</span><span style="color:#50FA7B">json</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Unrelated but somehow related note, you may notice in the snippet above that I used a <code>safeExec</code> function and a <code>Result</code> type. Those are tiny helpers I use across my projects, as I came to the realization that relying on throwing exceptions results in try/catch scattered everywhere, and can also, to some extent, result in uncaught issues. That's why I prefer to use a pattern like this, likely inherited from my work with Rust 🦀.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> type</span><span style="color:#8BE9FD;font-style:italic"> Result</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> { status</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">success</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">; result</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> T</span><span style="color:#F8F8F2"> } </span><span style="color:#FF79C6">|</span><span style="color:#F8F8F2"> { status</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">error</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">; err</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> unknown</span><span style="color:#F8F8F2"> };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> safeExec</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">>(</span><span style="color:#50FA7B">fn</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">>)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Result</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">>> </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> result </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> fn</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> { status</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">success</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, result };</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">err</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> unknown</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> { status</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">error</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, err };</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><hr />
<h2>Conclusion</h2>
<p>I'm a hobbyist, so don't expect ultra distances, marathon is about as far as I go, but if you are curious about the result you can see them live on the <a href="/trails" rel="noopener noreferrer external">Trails</a> page. Most routes I follow are convenient for day trips from Zürich. If you'd like to follow any of them and have questions, reach out!</p>
<p>Until next time!<br />
David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Lake Zurich]]></title>
          <link>https://daviddalbusco.com/trails/2026/lake-zurich/</link>
          <pubDate>Tue, 07 Jul 2026 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>I guess when you live in Zürich and own a bike, riding around the lake is basically mandatory for citizenship. Had to be done ;)</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[5-lake Pizol+]]></title>
          <link>https://daviddalbusco.com/trails/2026/5-lake-pizol-plus/</link>
          <pubDate>Fri, 03 Jul 2026 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>So, I spent the day in the fog 😄. I might redo it some day, the all view should be amazing, the lake and foggy scenery was already great.</p>
<p>The Pizol 5 lakes (5-Seen-Wanderung Pizol) is super popular, therefore best during the week if you don't want to slalom between hikers.</p>
<p>Passing the summit was alright but snowy. Likely a trail best suited towards the end of summer.</p>
<p>Oh, worth noting, had no phone signal most of the day.</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[A Progress Bar for Blog Posts]]></title>
          <description><![CDATA[Displaying how much of an article is left to read with a small Svelte component.]]></description>
          <link>https://daviddalbusco.com/blog/a-progress-bar-for-blog-posts/</link>
          <pubDate>Thu, 02 Jul 2026 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/and-machines-8gqqtZstztc-unsplash.jpg" alt="" / loading="lazy"></p>
<blockquote>
<p>Photo by <a href="https://unsplash.com/fr/@and_machines?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">and machines</a> on <a href="https://unsplash.com/fr/photos/fond-peint-degrade-bleu-avec-une-lueur-rose-8gqqtZstztc?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></p>
</blockquote>
<p>This morning, after doing my company's accounting and getting upset again at how garbage that software is, I decided I needed an easy, little coding task to chill 😄. So I added a small progress bar to the top of my blog posts. It's a nice little cosmetic win, and I thought I would follow up with a short post to share a couple of beginner friendly details.</p>
<blockquote>
<p>If you scroll this page, you should see a bar at the top fill up as you go, and reach 100% right as you finish reading.</p>
</blockquote>
<hr />
<h2>The component</h2>
<p>To add this new element to the blog I created a component. I might not reuse it elsewhere, but still, it's always nice to approach composition with agnostic, reusable components. It takes an <code>anchor</code> element (the end of the article) as a property, since we are looking to show the progression of the reading, computes this percentage, and renders it in a <code>&lt;progress&gt;</code> element pinned at the top.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#50FA7B;font-style:italic"> lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> { anchor }</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { anchor</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLElement</span><span style="color:#F8F8F2"> } </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> $props</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    $effect</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#6272A4">        // add and remove scroll listener, see "Why not svelte:window to bind scrollY" below</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> value </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> $state</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#50FA7B"> onscroll</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#6272A4">        // compute % scrolled relative to the anchor, see "The math" below</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">progress</span><span style="color:#50FA7B;font-style:italic"> {value}</span><span style="color:#50FA7B;font-style:italic"> max</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">100</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">progress</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">style</span><span style="color:#50FA7B;font-style:italic"> lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">scss</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    // </span><span style="color:#FF79C6">see "Styling" below</span></span>
<span class="line"><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2">/</span><span style="color:#FF79C6">style></span></span></code></pre><p>With the scene set, let's look at a few of the details.</p>
<hr />
<h2>Why not svelte:window to bind scrollY</h2>
<p>Svelte has a built-in way to bind to <code>scrollY</code> (<a href="https://svelte.dev/docs/svelte/svelte-window" rel="noopener noreferrer external">doc</a>).</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">svelte:window</span><span style="color:#50FA7B;font-style:italic"> bind</span><span style="color:#FF79C6">:</span><span style="color:#50FA7B;font-style:italic">scrollY</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">y</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2"> /></span></span></code></pre><p>AFAIK it doesn't let you pass <code>passive: true</code> to the underlying listener. I took a quick look at the source and it doesn't seem to use it either, though I'm not entirely sure. What I do know for sure is that using a passive listener for scroll is generally a performance best practice, even if in this particular case one might argue there isn't much to worry about. That's why I went with a custom event listener instead.</p>
<blockquote>
<p><strong>Update:</strong> turns out <code>passive</code> isn't actually needed here anyway. As pointed out in <a href="https://bsky.app/profile/antleth.fr/post/3mpqhzxheyk2a" rel="noopener noreferrer external">this thread</a> and confirmed by <a href="https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#using_passive_listeners" rel="noopener noreferrer external">MDN</a>, the <code>scroll</code> event can't be canceled, so a listener can't block page rendering regardless of <code>passive</code>.</p>
</blockquote>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">$effect</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    window.</span><span style="color:#50FA7B">addEventListener</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">scroll</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, onscroll, { passive</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2"> });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        window.</span><span style="color:#50FA7B">removeEventListener</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">scroll</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, onscroll, </span><span style="color:#BD93F9">false</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><p>In this effect I add and remove a scroll listener but you might notice a subtlety: <code>removeEventListener</code> doesn't pass the same options as <code>addEventListener</code>, which is generally required to correctly match and remove a listener. That was my assumption too, but TypeScript warned me those options weren't accepted. I looked into why and it turns out that's expected (see this <a href="https://github.com/microsoft/TypeScript/issues/32912" rel="noopener noreferrer external">issue</a>). Searching further I figured out <a href="https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener#matching_event_listeners_for_removal" rel="noopener noreferrer external">MDN's docs</a> explain it well: only <code>capture</code> matters for removal matching, not <code>passive</code>, so passing <code>false</code> here is just being explicit about <code>capture</code> rather than relying on its default.</p>
<hr />
<h2>The math</h2>
<p>It's common to see these bars track the full page scroll, but I wanted it to reflect the reading progress instead. Tracking <code>scrollY</code> against the full page height would be wrong here, because my pages have a footer. If I use the full scroll height, the bar never reaches 100% by the time you finish the article, since there's still the footer left to scroll past.</p>
<p>So instead I pass an <code>anchor</code> element (the end of the article), and compute how much of the scroll is &quot;left&quot; once you get there:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> delta </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> document.documentElement.scrollHeight </span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2"> anchor.offsetTop;</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> scrollLength </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> anchor.offsetTop </span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2"> delta;</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> y </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> (window.scrollY </span><span style="color:#FF79C6">*</span><span style="color:#BD93F9"> 100</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2"> scrollLength;</span></span></code></pre><p><code>delta</code> is everything below the anchor (footer included). Subtracting it from <code>anchor.offsetTop</code> gives the actual scroll distance that corresponds to &quot;reading the article&quot;, so the bar hits 100% right when you reach the end, not somewhere before it because of the footer.</p>
<p>Once I got the percentage of the progression (<code>y</code>), I could apply it to the state value of the <code>progress</code> element while capping it to avoid overflow.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">value </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> Math.</span><span style="color:#50FA7B">min</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">100</span><span style="color:#F8F8F2">, Math.</span><span style="color:#50FA7B">floor</span><span style="color:#F8F8F2">(y));</span></span></code></pre><hr />
<h2>Styling</h2>
<p>I used a native <code>&lt;progress&gt;</code> element instead of a styled <code>div</code>. Free accessibility semantics, and it, well, looks like a progress bar. I would have expected <code>accent-color</code> to be enough to style it, but it wasn't. I found the solution on <a href="https://stackoverflow.com/a/76860350/5404186" rel="noopener noreferrer external">Stack Overflow</a>, which I tailored for my website:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">progress</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD">    color</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD"> var</span><span style="color:#F8F8F2">(--color-highlight);</span></span>
<span class="line"><span style="color:#8BE9FD">    background</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> transparent</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    /* Firefox: Filled portion of the progress bar */</span></span>
<span class="line"><span style="color:#FF79C6">    &#x26;</span><span style="color:#FF79C6;font-style:italic">::</span><span style="color:#50FA7B;font-style:italic">-moz-progress-bar</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD">        background</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> currentColor</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    /* Chrome &#x26; Safari: Unfilled portion of the progress bar */</span></span>
<span class="line"><span style="color:#FF79C6">    &#x26;</span><span style="color:#FF79C6;font-style:italic">::</span><span style="color:#50FA7B;font-style:italic">-webkit-progress-bar</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD">        background</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> transparent</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    /* Chrome &#x26; Safari: Filled portion of the progress bar */</span></span>
<span class="line"><span style="color:#FF79C6">    &#x26;</span><span style="color:#FF79C6;font-style:italic">::</span><span style="color:#50FA7B;font-style:italic">-webkit-progress-value</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD">        background</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> currentColor</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>I set a base <code>color</code> and a transparent <code>background</code> that the pseudo-elements inherit through <code>currentColor</code>, but I still need an explicit <code>background</code> on the Chrome/Safari specific pseudo-elements, since neither browser applies the fill by default.</p>
<hr />
<h2>Summary</h2>
<p>Long story short, here's the full component:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#50FA7B;font-style:italic"> lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#FF79C6">    interface</span><span style="color:#8BE9FD;font-style:italic"> Props</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        anchor</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLElement</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> { anchor }</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Props</span><span style="color:#FF79C6"> =</span><span style="color:#50FA7B"> $props</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> value </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> $state</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#50FA7B"> onscroll</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> delta </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> document.documentElement.scrollHeight </span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2"> anchor.offsetTop;</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> scrollLength </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> anchor.offsetTop </span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2"> delta;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> y </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> (window.scrollY </span><span style="color:#FF79C6">*</span><span style="color:#BD93F9"> 100</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2"> scrollLength;</span></span>
<span class="line"><span style="color:#F8F8F2">        value </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> Math.</span><span style="color:#50FA7B">min</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">100</span><span style="color:#F8F8F2">, Math.</span><span style="color:#50FA7B">floor</span><span style="color:#F8F8F2">(y));</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    $effect</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        window.</span><span style="color:#50FA7B">addEventListener</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">scroll</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, onscroll, { passive</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2"> });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#6272A4">            // https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener#matching_event_listeners_for_removal</span></span>
<span class="line"><span style="color:#F8F8F2">            window.</span><span style="color:#50FA7B">removeEventListener</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">scroll</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, onscroll, </span><span style="color:#BD93F9">false</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">        };</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">progress</span><span style="color:#50FA7B;font-style:italic"> {value}</span><span style="color:#50FA7B;font-style:italic"> max</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">100</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">progress</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">style</span><span style="color:#50FA7B;font-style:italic"> lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">scss</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#FF79C6">    progress</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD">        position</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> fixed</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">        top</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">        left</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">        width</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 100</span><span style="color:#FF79C6">%</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">        height</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 0.25</span><span style="color:#FF79C6">rem</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">        z-index</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 10</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">        appearance</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> none</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">        border</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> none</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    // </span><span style="color:#FF79C6">Color: https:</span><span style="color:#F8F8F2">//</span><span style="color:#FF79C6">stackoverflow.com</span><span style="color:#F8F8F2">/</span><span style="color:#FF79C6">a</span><span style="color:#F8F8F2">/76860350/5404186</span></span>
<span class="line"><span style="color:#FF79C6">    progress</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD">        color</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD"> var</span><span style="color:#F8F8F2">(--color-highlight);</span></span>
<span class="line"><span style="color:#8BE9FD">        background</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> transparent</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">        /* Firefox: Filled portion of the progress bar */</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x26;</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2">:</span><span style="color:#BD93F9">-moz-progress-bar</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span>            </span><span style="color:#F8F8F2;font-style:italic;text-decoration:underline">background</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">currentColor</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">        /* Chrome &#x26; Safari: Unfilled portion of the progress bar */</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x26;</span><span style="color:#FF79C6;font-style:italic">::</span><span style="color:#50FA7B;font-style:italic">-webkit-progress-bar</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD">            background</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> transparent</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">        /* Chrome &#x26; Safari: Filled portion of the progress bar */</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x26;</span><span style="color:#FF79C6;font-style:italic">::</span><span style="color:#50FA7B;font-style:italic">-webkit-progress-value</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD">            background</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> currentColor</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">style</span><span style="color:#F8F8F2">></span></span></code></pre><hr />
<h2>Conclusion</h2>
<p>Nothing fancy but, it was fun. Feel free to copy/pasta the code or let me know if anything should be improved.</p>
<p>Until next time!<br />
David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Greifensee]]></title>
          <link>https://daviddalbusco.com/trails/2026/greifensee/</link>
          <pubDate>Tue, 30 Jun 2026 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>A loop around the Greifensee.</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Altdorf - Engelberg]]></title>
          <link>https://daviddalbusco.com/trails/2026/altdorf-engelberg/</link>
          <pubDate>Sat, 27 Jun 2026 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>Poor performance. Two thirds felt like running in an oven thanks to the heatwave, and the last third was spent hoping not to get caught in the storm.</p>
<p>Around 200m of the pass was covered in snow. July or even August feels like the safer bet if you want to avoid it.</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Yet Another Web Analytics]]></title>
          <description><![CDATA[A deep dive into yawa, a self-hosted analytics tool I built with an MCP instead of a UI.]]></description>
          <link>https://daviddalbusco.com/blog/yet-another-web-analytics/</link>
          <pubDate>Wed, 24 Jun 2026 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/boliviainteligente-uiJNYtAbfVU-unsplash.jpg" alt="" / loading="lazy"></p>
<blockquote>
<p>Photo by <a href="https://unsplash.com/fr/@boliviainteligente?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">BoliviaInteligente</a> on <a href="https://unsplash.com/fr/photos/un-fond-abstrait-colore-avec-des-lignes-ondulees-uiJNYtAbfVU?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></p>
</blockquote>
<p>I've been building <a href="https://github.com/junobuild/juno" rel="noopener noreferrer external">Juno</a> for a few years, a platform I ultimately deprecated recently. As a result, I needed to move my projects away from it, including the analytics of my own website, which were baked into the platform.</p>
<p>So I looked around at what was out there that matched my requirements (privacy-friendly, self-hosted, open-source, not sloppy, etc.), found a few good options, but ultimately thought: why not build my own web analytics, again 😅. After all, I already had the knowledge and could likely reuse some of my code.</p>
<p>I gave it some thought but honestly was still hesitant, until a second motivation came into play: what if I explored a new UX paradigm? What if instead of building a UI, I would solely rely on an LLM (Claude Code or else) to query my analytics in plain language (also known as &quot;frenglish&quot; in my case).</p>
<p>The result is <strong><a href="https://yetanotherwebanalytics.dev" rel="noopener noreferrer external">yet another web analytics</a></strong> (shortened yawa by the cool kids, meaning me), a self-hostable, open-source, no cookies, no GDPR consent, web analytics solution.</p>
<hr />
<h2>How it works</h2>
<p>Your <strong>app or website</strong> sends analytics through a lightweight JavaScript library (<code>yawa-tracker</code>) that collects page views, custom events, and Web Vitals.</p>
<p>On <strong>your self-hosted server</strong>, a Docker container runs an API that receives the events and stores them in an embedded <a href="https://duckdb.org" rel="noopener noreferrer external">DuckDB</a> database. It also exposes 26+ analytics tools over the <a href="https://modelcontextprotocol.io" rel="noopener noreferrer external">Model Context Protocol (MCP)</a>.</p>
<p>Finally, connect your <strong>MCP client</strong> (Claude Code, Codex, or any compatible client) to your endpoint and query your data in plain language.</p>
<p>In practice, that last step looks like this:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">❯ What are my top pages </span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2"> week</span><span style="color:#FF79C6">?</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">• Fetching your site ID and top pages for </span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2"> week.</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  Called fluster</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">analytics </span><span style="color:#BD93F9">2</span><span style="color:#F8F8F2"> times</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">• Here are your top </span><span style="color:#BD93F9">10</span><span style="color:#F8F8F2"> pages on daviddalbusco.com </span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#50FA7B"> week</span><span style="color:#F8F8F2"> (Jun </span><span style="color:#BD93F9">15</span><span style="color:#F8F8F2">–</span><span style="color:#BD93F9">22</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">┌───────────────────────────────────────────────────────────────┬──────────┬───────────┐</span></span>
<span class="line"><span style="color:#F8F8F2">│                             Page                              │ Visitors │ Pageviews │</span></span>
<span class="line"><span style="color:#F8F8F2">├───────────────────────────────────────────────────────────────┼──────────┼───────────┤</span></span>
<span class="line"><span style="color:#F8F8F2">│ </span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2"> (home)                                                      │ </span><span style="color:#BD93F9">72</span><span style="color:#F8F8F2">       │ </span><span style="color:#BD93F9">84</span><span style="color:#F8F8F2">        │</span></span>
<span class="line"><span style="color:#F8F8F2">├───────────────────────────────────────────────────────────────┼──────────┼───────────┤</span></span>
<span class="line"><span style="color:#F8F8F2">│ </span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2">blog</span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2">making</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">your</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">website</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">ai</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">ready                            │ </span><span style="color:#BD93F9">53</span><span style="color:#F8F8F2">       │ </span><span style="color:#BD93F9">59</span><span style="color:#F8F8F2">        │</span></span>
<span class="line"><span style="color:#F8F8F2">├───────────────────────────────────────────────────────────────┼──────────┼───────────┤</span></span>
<span class="line"><span style="color:#F8F8F2">│ </span><span style="color:#FF5555">/</span><span style="color:#F1FA8C">blog</span><span style="color:#FF5555">/</span><span style="color:#F8F8F2">                                                        │ </span><span style="color:#BD93F9">14</span><span style="color:#F8F8F2">       │ </span><span style="color:#BD93F9">14</span><span style="color:#F8F8F2">        │</span></span>
<span class="line"><span style="color:#F8F8F2">├───────────────────────────────────────────────────────────────┼──────────┼───────────┤</span></span>
<span class="line"><span style="color:#F8F8F2">│ </span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2">blog</span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2">automating</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">newsletter</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">claude</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">telegram</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">cloudflare</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">worker  │ </span><span style="color:#BD93F9">10</span><span style="color:#F8F8F2">       │ </span><span style="color:#BD93F9">11</span><span style="color:#F8F8F2">        │</span></span>
<span class="line"><span style="color:#F8F8F2">├───────────────────────────────────────────────────────────────┼──────────┼───────────┤</span></span>
<span class="line"><span style="color:#F8F8F2">│ </span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2">blog</span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2">making</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">your</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">website</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">ai</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">ready</span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2">                           │ </span><span style="color:#BD93F9">10</span><span style="color:#F8F8F2">       │ </span><span style="color:#BD93F9">11</span><span style="color:#F8F8F2">        │</span></span>
<span class="line"><span style="color:#F8F8F2">├───────────────────────────────────────────────────────────────┼──────────┼───────────┤</span></span>
<span class="line"><span style="color:#F8F8F2">│ </span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2">blog</span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2">better</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">type</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">safety</span><span style="color:#FF79C6">-in-</span><span style="color:#F8F8F2">svelte</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">guard</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">components           │ </span><span style="color:#BD93F9">6</span><span style="color:#F8F8F2">        │ </span><span style="color:#BD93F9">6</span><span style="color:#F8F8F2">         │</span></span>
<span class="line"><span style="color:#F8F8F2">├───────────────────────────────────────────────────────────────┼──────────┼───────────┤</span></span>
<span class="line"><span style="color:#F8F8F2">│ </span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2">blog</span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2">serving</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">local</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">assets</span><span style="color:#FF79C6">-with-</span><span style="color:#F8F8F2">bun                           │ </span><span style="color:#BD93F9">6</span><span style="color:#F8F8F2">        │ </span><span style="color:#BD93F9">6</span><span style="color:#F8F8F2">         │</span></span>
<span class="line"><span style="color:#F8F8F2">└───────────────────────────────────────────────────────────────┴──────────┴───────────┘</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">The </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Making your website AI-ready</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> post is clearly the standout </span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2"> week </span><span style="color:#FF79C6">with</span><span style="color:#BD93F9"> 53</span><span style="color:#F8F8F2"> visitors — well ahead </span><span style="color:#FF79C6">of</span><span style="color:#F8F8F2"> everything </span><span style="color:#FF79C6">else</span><span style="color:#F8F8F2"> after the homepage.</span></span></code></pre><p>No dashboard. No login (though obviously access is restricted via bearer token). Just a question and an answer (and a few tokens spent, nothing is perfect).</p>
<hr />
<h2>Architecture</h2>
<p>One might argue I overengineered it, to which I would answer: I build tools that aim to be maintainable. So even for a side project, I gave the repo a proper structure and split it into focused packages with clear separation of concerns.</p>
<p>For example, the database layer is intentionally isolated. If I ever swap <a href="https://bun.sh" rel="noopener noreferrer external">Bun</a> or the <a href="https://hono.dev" rel="noopener noreferrer external">Hono</a> API engine, I won't have to rewrite the entire data layer.</p>
<ul>
<li><a href="https://github.com/peterpeterparker/yawa/tree/main/packages/app" rel="noopener noreferrer external">app</a> - the server with two sub-apps: a public API (<code>/events/*</code>, <code>/mcp</code>) for collecting events and providing analytics data, and an internal API available solely within the container (see next chapter)</li>
<li><a href="https://github.com/peterpeterparker/yawa/tree/main/packages/cli" rel="noopener noreferrer external">cli</a> - the admin CLI for token and site management</li>
<li><a href="https://github.com/peterpeterparker/yawa/tree/main/packages/db" rel="noopener noreferrer external">db</a> - DuckDB instance management, queries, and migrations (yes, I had to write that part too)</li>
<li><a href="https://github.com/peterpeterparker/yawa/tree/main/packages/schema" rel="noopener noreferrer external">schema</a> - shared Zod schemas and types</li>
<li><a href="https://github.com/peterpeterparker/yawa/tree/main/packages/common" rel="noopener noreferrer external">common</a> - shared utilities (<code>Result</code>, <code>Option</code>, error helpers)</li>
<li><a href="https://github.com/peterpeterparker/yawa/tree/main/packages/tracker" rel="noopener noreferrer external">tracker</a> - the JavaScript library for your frontend, published to npm</li>
</ul>
<p>At the root of the repo there is the traditional zillion of configuration files any project requires nowadays, plus the <a href="https://github.com/peterpeterparker/yawa/blob/main/Dockerfile.app" rel="noopener noreferrer external">Dockerfile</a> used to bundle and publish the image to <a href="https://hub.docker.com/r/peterpeterparker/yawa" rel="noopener noreferrer external">Docker Hub</a>.</p>
<hr />
<h2>Interesting bits</h2>
<p>Most of the implementation was straightforward, and I'll be honest, I also used an LLM to help generate repetitive patterns under my supervision, I'm not that much of a caveman. But there were a few challenges and interesting gotchas I thought, or I hope, are worth sharing.</p>
<h3>The CLI and the internal API</h3>
<p>DuckDB allows only one read-write connection at a time (see <a href="https://duckdb.org/docs/current/connect/concurrency" rel="noopener noreferrer external">documentation</a>). Since the CLI and the server run simultaneously (that's just normal usage), having the CLI open the database directly would cause a file lock error. So I had to figure out a way to work around the limitation.</p>
<p>The solution: don't instantiate the database in the CLI at all. Instead, have it talk to the server, which owns the database exclusively.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">yawa</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">cli → POST </span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2">tokens → yawa</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">app → DuckDB</span></span></code></pre><p>That raised another question though: exposing CLI endpoints in the API would open private features to the world. Not great.</p>
<p>That's where the idea of having an internal API came in, one that shares the same process as the public API and by extension the same database connection.</p>
<p>I set up a second Hono app that runs on port 9999 but bound to <code>127.0.0.1</code> via <code>Bun.serve</code>'s <a href="https://bun.sh/docs/runtime/http/server#changing-the-port-and-hostname" rel="noopener noreferrer external">hostname</a> option. That way, no external requests can reach it. To make this even more explicit, the docs show a <code>docker-compose.yml</code> that never maps the port, and a Caddy config that never proxies it.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">public   → https:</span><span style="color:#6272A4">//yourdomain:3000 → yawa-app (public)   ─┐</span></span>
<span class="line"><span style="color:#F8F8F2">                                                          ├→ DuckDB</span></span>
<span class="line"><span style="color:#F8F8F2">yawa</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">cli → http:</span><span style="color:#6272A4">//localhost:9999   → yawa-app (internal) ─┘</span></span></code></pre><p>Since both run together and the CLI accesses it through plain fetch requests, the locking and coordination problem was solved.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">Bun.</span><span style="color:#50FA7B">serve</span><span style="color:#F8F8F2">({ port</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 3000</span><span style="color:#F8F8F2">, fetch</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> appFetch, hostname</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">0.0.0.0</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> }); </span><span style="color:#6272A4">// public</span></span>
<span class="line"><span style="color:#F8F8F2">Bun.</span><span style="color:#50FA7B">serve</span><span style="color:#F8F8F2">({ port</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 9999</span><span style="color:#F8F8F2">, fetch</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> internalFetch, hostname</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">127.0.0.1</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> }); </span><span style="color:#6272A4">// internal only</span></span></code></pre><h3>DB migrations</h3>
<p>Unfortunately, neither Drizzle nor Prisma support DuckDB yet. Since the schema will obviously evolve over time, I had to write a small migration runner myself, which you can find <a href="https://github.com/peterpeterparker/yawa/blob/main/packages/db/src/migrate.ts" rel="noopener noreferrer external">here</a>.</p>
<p>The script runs each time the app starts and looks for plain SQL files in a directory, files that create schemas, tables, add new columns, etc.</p>
<p>It then sorts and filters out the files that have already been applied by comparing them against those recorded in the database. To keep track of migrations, I created a table that lists the scripts that were used to terraform it.</p>
<p>It applies the remaining scripts and records them.</p>
<p>Nothing fancy, but it does the job.</p>
<h3>Session hashing without cookies</h3>
<p>For privacy-friendly visitor counting, I looked at how <a href="https://umami.is" rel="noopener noreferrer external">Umami</a> does it (e.g. <a href="https://github.com/umami-software/umami/blob/c0ea3aefbee7a3429ee2f824b06dc4a9dbe0b7e1/src/app/api/send/route.ts#L311" rel="noopener noreferrer external">source</a>). The approach is to hash a combination of site ID, IP address, user agent, and a daily salt into a deterministic session ID. Same visitor, same day, same session. No cookie required.</p>
<p>The IP address is extracted from the request headers, checking a list of known CDN headers and notably <code>x-forwarded-for</code> included by <a href="https://caddyserver.com/docs/caddyfile/directives/reverse_proxy#defaults" rel="noopener noreferrer external">Caddy</a>), the reverse-proxy I used to expose the API with a custom domain.</p>
<p>The session ID itself is computed like this:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { CryptoHasher } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">bun</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> hash</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">input</span><span style="color:#F8F8F2"> }</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { input</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2"> })</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { hash</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2"> } </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> ({</span></span>
<span class="line"><span style="color:#F8F8F2">    hash</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> CryptoHasher</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">sha256</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">).</span><span style="color:#50FA7B">update</span><span style="color:#F8F8F2">(input).</span><span style="color:#50FA7B">digest</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">hex</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> { hash</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> salt } </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> hash</span><span style="color:#F8F8F2">({ input</span><span style="color:#FF79C6">:</span><span style="color:#50FA7B"> startOfDay</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6;font-weight:bold">new</span><span style="color:#50FA7B"> Date</span><span style="color:#F8F8F2">()).</span><span style="color:#50FA7B">toISOString</span><span style="color:#F8F8F2">() });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> { hash</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> sessionHash } </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> hash</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    input</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> `</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">site_id</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">|</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">ip</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">|</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">user_agent</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">|</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">salt</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">|</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">sessionSecret</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> sessionId </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> Bun.</span><span style="color:#50FA7B">randomUUIDv5</span><span style="color:#F8F8F2">(sessionHash, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">dns</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span></code></pre><p>The salt rotates daily so sessions do not persist across days. The secret (<code>YAWA_SESSION_SECRET</code>) adds an extra layer so the hash cannot be reverse-engineered. If not set, it falls back to random UUIDs, which means less accurate visitor counting, but the app still works. Handy for development.</p>
<p>From an architecture point of view, both the IP and session ID are injected into the request context via middlewares, so route handlers never have to extract them manually, and errors are handled before the handler is ever reached.</p>
<p>For example:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> extractIpMiddleware </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> createMiddleware</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">AnalyticsApiEnv</span><span style="color:#F8F8F2">>(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">context</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">next</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        req</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">            raw</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { headers }</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> context;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    context.</span><span style="color:#50FA7B">set</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ip</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#50FA7B">getIp</span><span style="color:#F8F8F2">({ headers }));</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#50FA7B"> next</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><h3>Bun and Docker issue with DuckDB bindings</h3>
<p>This one took a while to figure out. DuckDB uses native <code>.node</code> bindings, which causes multiple issues when bundling with Bun and run in Docker. I ended up posting my findings on the <a href="https://github.com/duckdb/duckdb-node-neo/issues/231" rel="noopener noreferrer external">issue</a> tracker, but here's the summary.</p>
<p><strong>1. Building locally fails</strong> because Bun tries to resolve all platform variants at build time, including Windows bindings not installed on my macOS. The fix is to mark every binding that doesn't match the current platform as external.</p>
<p><strong>2. Using <code>--compile</code> fails at runtime in Docker</strong> with <code>libduckdb.so: cannot open shared object file</code>. The native <code>.so</code> simply cannot be embedded in the compiled binary. The only solution was to drop <code>--compile</code> entirely.</p>
<p><strong>3. Only marking platform-specific bindings as external is not enough.</strong> <code>@duckdb/node-api</code> and <code>@duckdb/node-bindings</code> themselves must also be external, otherwise the bundled output fails to resolve the native module at runtime.</p>
<p><strong>4. In a monorepo, DuckDB must be a root dependency.</strong> If it lives only in a workspace package, Docker won't resolve the bindings correctly.</p>
<p>In other words, I needed a custom build script for Bun:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> os </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">node:os</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// Building duckdb with Bun does not work.</span></span>
<span class="line"><span style="color:#6272A4">// see https://github.com/duckdb/duckdb-node-neo/issues/231</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> platform </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> os.</span><span style="color:#50FA7B">platform</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> arch </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> os.</span><span style="color:#50FA7B">arch</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// https://github.com/duckdb/duckdb-node-neo#documentation</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> duckdbPlatformBindings </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">@duckdb/node-bindings-darwin-arm64</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">@duckdb/node-bindings-darwin-x64</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">@duckdb/node-bindings-linux-arm64</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">@duckdb/node-bindings-linux-arm64-musl</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">@duckdb/node-bindings-linux-x64</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">@duckdb/node-bindings-linux-x64-musl</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">@duckdb/node-bindings-win32-arm64</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">@duckdb/node-bindings-win32-x64</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// We need to set all bindings as external and Bun should not use the --compile option.</span></span>
<span class="line"><span style="color:#6272A4">// Furthermore, the node_modules need to be copied in Docker.</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> duckdbBindings </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">@duckdb/node-api</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">@duckdb/node-bindings</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// oven/bun is Debian-based so musl is not needed in Docker and I develop on Mac.</span></span>
<span class="line"><span style="color:#6272A4">// If its support is needed in the future, e.g. detect-libc could be used to detect it.</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> current </span><span style="color:#FF79C6">=</span><span style="color:#F1FA8C"> `@duckdb/node-bindings-</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">platform</span><span style="color:#FF79C6"> ===</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">win32</span><span style="color:#E9F284">"</span><span style="color:#FF79C6"> ?</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">win32</span><span style="color:#E9F284">"</span><span style="color:#FF79C6"> :</span><span style="color:#F8F8F2"> platform</span><span style="color:#FF79C6"> ===</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">darwin</span><span style="color:#E9F284">"</span><span style="color:#FF79C6"> ?</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">darwin</span><span style="color:#E9F284">"</span><span style="color:#FF79C6"> :</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">linux</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">-</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">arch</span><span style="color:#FF79C6"> ===</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">x64</span><span style="color:#E9F284">"</span><span style="color:#FF79C6"> ?</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">x64</span><span style="color:#E9F284">"</span><span style="color:#FF79C6"> :</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">arm64</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> platformSpecificExternals </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> duckdbPlatformBindings.</span><span style="color:#50FA7B">filter</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">binding</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> binding </span><span style="color:#FF79C6">!==</span><span style="color:#F8F8F2"> current);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">await</span><span style="color:#F8F8F2"> Bun.</span><span style="color:#50FA7B">build</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    entrypoints</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">./src/index.ts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">    outdir</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./build</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    target</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">bun</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    minify</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    external</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#FF79C6">...</span><span style="color:#F8F8F2">duckdbBindings, </span><span style="color:#FF79C6">...</span><span style="color:#F8F8F2">platformSpecificExternals]</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><p>And the relevant Dockerfile snippet:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">...</span></span>
<span class="line"><span style="color:#6272A4"># build final image</span></span>
<span class="line"><span style="color:#FF79C6">FROM</span><span style="color:#F8F8F2"> base </span><span style="color:#FF79C6">AS</span><span style="color:#F8F8F2"> release</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4"># We need to copy native bindings (e.g. DuckDB) from prod node_modules</span></span>
<span class="line"><span style="color:#6272A4"># otherwise we cannot start the db.</span></span>
<span class="line"><span style="color:#6272A4"># error: libduckdb.so: cannot open shared object file: No such file or directory</span></span>
<span class="line"><span style="color:#FF79C6">COPY</span><span style="color:#F8F8F2"> --from=install /temp/prod/node_modules ./node_modules</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">COPY</span><span style="color:#F8F8F2"> --from=prerelease /usr/src/app/packages/app/build/index.js ./index.js</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">...</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">CMD</span><span style="color:#F8F8F2"> [</span><span style="color:#F1FA8C">"bun"</span><span style="color:#F8F8F2">, </span><span style="color:#F1FA8C">"index.js"</span><span style="color:#F8F8F2">]</span></span></code></pre><h3>Handler typing</h3>
<p>Hono recommends against <a href="https://hono.dev/docs/guides/best-practices#don-t-make-controllers-when-possible" rel="noopener noreferrer external">Rails-style controllers</a> because extracting handlers into separate functions loses type inference - path params, validated body, and env variables all become untyped <code>Context</code>.</p>
<p>For a project with a few routes this is fine, but yawa has two separate apps (public and internal), each with their own middleware-injected env types. Inlining everything would get messy and, generally speaking, I find it cleaner to have separate controllers. Furthermore, it makes testing easier. So I wanted named, importable handlers.</p>
<p>The trick, found via <a href="https://stackoverflow.com/a/79300489/5404186" rel="noopener noreferrer external">this Stack Overflow answer</a>, is to type the handler with the input schema shape that <code>zValidator</code> produces:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">type</span><span style="color:#8BE9FD;font-style:italic"> JsonInputSchema</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#FF79C6"> extends</span><span style="color:#FFB86C;font-style:italic"> z</span><span style="color:#F8F8F2">.</span><span style="color:#FFB86C;font-style:italic">ZodType</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    in</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { json</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> z</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">input</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">> };</span></span>
<span class="line"><span style="color:#F8F8F2">    out</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { json</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> z</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">infer</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">> };</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> type</span><span style="color:#8BE9FD;font-style:italic"> DefineHandler</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#FF79C6"> extends</span><span style="color:#FFB86C;font-style:italic"> z</span><span style="color:#F8F8F2">.</span><span style="color:#FFB86C;font-style:italic">ZodType</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#FFB86C;font-style:italic">    context</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Context</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Env</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">JsonInputSchema</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">>></span></span>
<span class="line"><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Option</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Response</span><span style="color:#F8F8F2">>>;</span></span></code></pre><p>Given a schema like:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> CreateSiteRequestSchema </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> z.</span><span style="color:#50FA7B">object</span><span style="color:#F8F8F2">({ hostname</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> z.</span><span style="color:#50FA7B">string</span><span style="color:#F8F8F2">() });</span></span></code></pre><p>A handler becomes:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> defineCreateSite</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> DefineHandler</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">typeof</span><span style="color:#F8F8F2"> CreateSiteRequestSchema> </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">context</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        req,</span></span>
<span class="line"><span style="color:#F8F8F2">        var</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">            db</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { connection }</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> context;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { hostname } </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> req.</span><span style="color:#50FA7B">valid</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">json</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">); </span><span style="color:#6272A4">// fully typed</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // ...</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Registered as:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">app.</span><span style="color:#50FA7B">post</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/sites</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#50FA7B">zValidator</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">json</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, CreateSiteRequestSchema), defineCreateSite);</span></span></code></pre><p><code>req.valid(&quot;json&quot;)</code> is fully typed, the env variables are typed, and the handler is cleanly importable. For handlers with no JSON body, <code>DefineHandler&lt;never&gt;</code> works as a fallback.</p>
<p>As for the variables injected by middlewares, they can be declared by extending the environment.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> type</span><span style="color:#8BE9FD;font-style:italic"> ApiEnv</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> { Variables</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { db</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { connection</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> DbConnection</span><span style="color:#F8F8F2"> } } };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> type</span><span style="color:#8BE9FD;font-style:italic"> DefineHandler</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#FF79C6"> extends</span><span style="color:#FFB86C;font-style:italic"> z</span><span style="color:#F8F8F2">.</span><span style="color:#FFB86C;font-style:italic">ZodType</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">Env</span><span style="color:#FF79C6"> extends</span><span style="color:#FFB86C;font-style:italic"> ApiEnv</span><span style="color:#FF79C6"> =</span><span style="color:#FFB86C;font-style:italic"> ApiEnv</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#FFB86C;font-style:italic">    context</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Context</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Env</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">JsonInputSchema</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">>></span></span>
<span class="line"><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Option</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Response</span><span style="color:#F8F8F2">>>;</span></span></code></pre><h3>The tracker script</h3>
<p>My first version of the tracker was only available as an npm package, which made sense for apps using a bundler. But when I started writing the yawa docs with Astro, I realized the idiomatic way to add analytics there was a plain <code>&lt;script&gt;</code> tag. I looked at how other analytics platforms handle this and most indeed default to such a script.</p>
<p>I didn't want to drop the npm package, but I also didn't want to duplicate the tracker logic. So my solution was to add an API route that dynamically generates a small bootstrap script, which imports and initializes the tracker from the server's own static assets.</p>
<p>The fun part? The route figures out its own server URL automatically, including handling reverse proxy headers like <code>x-forwarded-proto</code>, so no hardcoded configuration is needed:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> defineTracker</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> DefineHandler</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">z</span><span style="color:#F8F8F2">.</span><span style="color:#FFB86C;font-style:italic">ZodType</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">context</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { protocol</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> urlProtocol, host } </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> URL</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">parse</span><span style="color:#F8F8F2">(context.req.url);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> proxiedProto </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> context.req.</span><span style="color:#50FA7B">header</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">x-forwarded-proto</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> protocol </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> notEmptyString</span><span style="color:#F8F8F2">(proxiedProto) </span><span style="color:#FF79C6">?</span><span style="color:#F1FA8C"> `</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">proxiedProto</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">:`</span><span style="color:#FF79C6"> :</span><span style="color:#F8F8F2"> urlProtocol;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> serverUrl </span><span style="color:#FF79C6">=</span><span style="color:#F1FA8C"> `</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">protocol</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">//</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">host</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> script </span><span style="color:#FF79C6">=</span><span style="color:#F1FA8C"> `import { init } from '</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">serverUrl</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">/static/yawa/dist/index.js';init({serverUrl:"</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">serverUrl</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">"});`</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> context.</span><span style="color:#50FA7B">text</span><span style="color:#F8F8F2">(script, </span><span style="color:#BD93F9">200</span><span style="color:#F8F8F2">, {</span></span>
<span class="line"><span style="color:#E9F284">        "</span><span style="color:#F1FA8C">Content-Type</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">application/javascript; charset=utf-8</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">        "</span><span style="color:#F1FA8C">x-content-type-options</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">nosniff</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">        "</span><span style="color:#F1FA8C">Cache-Control</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">public, max-age=604800</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>The tracker itself is then built and bundled into the Docker container, without forgetting to precompress its resources with Brotli for best performance at runtime.</p>
<p>This way, everything remains self-hosted including the client side script.</p>
<hr />
<h2>Conclusion</h2>
<p>I'm a bit amazed this all works after only a few days of development. The various issues, from the MCP server setup to the DuckDB build pipeline, were good learning experiences. But what I'm most curious about is the UX. This way of interacting with statistics is genuinely interesting, and even if I still sometimes catch myself thinking &quot;it would be nice to have a dashboard&quot;, I'm really curious to see if over time that feeling will disappear.</p>
<p>As for the project itself, I built it for myself but feel free to use it. The source is on <a href="https://github.com/peterpeterparker/yawa" rel="noopener noreferrer external">GitHub</a> and the Docker image is on Docker Hub. I don't plan to add features like user journeys or funnels because I simply don't need them, but feel free to contribute, or <a href="https://daviddalbusco.com" rel="noopener noreferrer external">hire me</a> if you need something more specific.</p>
<p>Until next time!<br />
David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Zurich - Baden loop]]></title>
          <link>https://daviddalbusco.com/trails/2026/zurich-baden-loop/</link>
          <pubDate>Wed, 24 Jun 2026 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>A loop around the Greifensee.</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Brisen]]></title>
          <link>https://daviddalbusco.com/trails/2026/brisen/</link>
          <pubDate>Sat, 20 Jun 2026 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>Loop from Dallenwil to Brisen.</p>
<p>No snow this year, therefore the trail was easy to follow.</p>
<p>There are ways to reduce the effort by taking cable cars to Niederrickenbach or Haldigrat, but 12/10, really enjoyed it!</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Making Your Website AI-Ready]]></title>
          <description><![CDATA[A few tips to help your site become LLM-friendly (or not).]]></description>
          <link>https://daviddalbusco.com/blog/making-your-website-ai-ready/</link>
          <pubDate>Thu, 18 Jun 2026 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/maxim-berg-qsDfqZyTCAE-unsplash.jpg" alt="" / loading="lazy"></p>
<blockquote>
<p>Photo by <a href="https://unsplash.com/fr/@maxberg?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Maxim Berg</a> on <a href="https://unsplash.com/fr/photos/un-fond-multicolore-avec-des-lignes-de-differentes-couleurs-qsDfqZyTCAE?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></p>
</blockquote>
<p>I've been gradually trying to make my project, <a href="https://juno.build" rel="noopener noreferrer external">Juno</a>, more AI-friendly. One might argue I failed since it's now deprecated (#yolo), but since I navigated a whole bunch of various pseudo-standard specifications (spoiler alert: every major corporation just goes &quot;here are our standards&quot;), I figured I'd share what I found, hoping it might help you too.</p>
<hr />
<h2>llms.txt</h2>
<p>The starting point is <a href="https://llmstxt.org/" rel="noopener noreferrer external">llmstxt.org</a>, a proposal by Jeremy Howard that defines a <code>/llms.txt</code> file living at the root of your site. Think of it like <code>sitemap.xml</code>, but for AI tools rather than search crawlers: a structured Markdown document that gives agents a curated entry point to your site.</p>
<p>That said, it's worth noting that it recently started to be challenged. Astro notably removed it, arguing they didn't notice much traffic requesting those files (see PR <a href="https://github.com/withastro/docs/pull/13538" rel="noopener noreferrer external">#13538</a>). So I'm not really sure if LLMs are still considering it nowadays.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span># Juno</span></span>
<span class="line"><span></span></span>
<span class="line"><span>> Juno is a full-stack platform to develop, deploy, and run apps in WASM containers with zero DevOps.</span></span>
<span class="line"><span></span></span>
<span class="line"><span>## Docs</span></span>
<span class="line"><span></span></span>
<span class="line"><span>- [Getting started](https://juno.build/docs/intro.md): Introduction and quick start</span></span>
<span class="line"><span>- [CLI reference](https://juno.build/docs/cli.md): All CLI commands</span></span>
<span class="line"><span></span></span>
<span class="line"><span>## Build</span></span>
<span class="line"><span></span></span>
<span class="line"><span>- [Functions](https://juno.build/docs/build/functions.md): Learn how to develop Serverless Functions</span></span></code></pre><blockquote>
<p>While the link descriptions are marked as optional in the spec, in practice I found that omitting them degraded the output of the tools I used to test my files.</p>
</blockquote>
<h3>llms-full.txt</h3>
<p>When implementing this standard, you can (or should, probably) also generate a companion <code>llms-full.txt</code> that expands all those links into a single document with the full content, handy when you want to give an AI everything at once rather than making it follow links.</p>
<h3>Markdown versions of each page</h3>
<p>The spec also suggests providing a clean Markdown version of every page at the same URL with <code>.md</code> appended. So <code>/blog/my-post</code> (respectively <code>/blog/my-post/index.html</code> or <code>/blog/my-post.html</code>) would have a Markdown twin at <code>/blog/my-post.md</code>.</p>
<p>This is useful beyond the spec itself. When an AI agent fetches a page and gets back clean prose instead of HTML, it has a much better time extracting the actual content.</p>
<h3>Alternate rel meta tags</h3>
<p>While I did not find it in the spec, I noticed other sites were also adding a <code>&lt;link rel=&quot;alternate&quot; type=&quot;text/markdown&quot; href=&quot;/blog/my-post.md&quot; /&gt;</code> tag to the <code>&lt;head&gt;</code> of each page. It's a signal agents can pick up when they land on the HTML version: there is a cleaner format available.</p>
<h3>Generating the files</h3>
<p>No one is going to maintain those files by hand, right? The common approach is to use an existing plugin for your documentation framework that takes care of generating them on each build.</p>
<p>In my case, I ended up writing my own Docusaurus plugin, because I always pay attention to tech debt, preferring to do it myself when the effort/cost ratio makes sense long term, and it gave me the flexibility to control the output with the fine grain I wanted. For example, I decided not to include blog posts in <code>llms.txt</code> since they would duplicate snippets already covered by the documentation. Whether that actually matters to an AI is anyone's guess, but it felt cleaner.</p>
<p>Since the files are generated at build time, I also set up a GitHub Action that commits a snapshot of them to the repo on each run. That way, on every PR, I can visually check whether the changes were applied as expected.</p>
<ul>
<li><a href="https://github.com/junobuild/docs/blob/main/plugins/docusaurus.llms.plugin.ts" rel="noopener noreferrer external">Docusaurus plugin</a></li>
<li><a href="https://github.com/junobuild/docs/blob/main/.github/workflows/llms.yml" rel="noopener noreferrer external">GitHub Action</a></li>
</ul>
<h3>Link header pointing to llms.txt</h3>
<p>Last on this topic, in the HTTP response headers of the root page:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">Link: </span><span style="color:#FF79C6">&#x3C;/</span><span style="color:#F8F8F2">llms.txt</span><span style="color:#FF79C6">></span><span style="color:#F8F8F2">; rel</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">describedby</span><span style="color:#E9F284">"</span></span></code></pre><p>A few crawlers and tools already know to look for this, as I learned by using an assertion tool I'll share below in the last chapter.</p>
<h3>Summary</h3>
<p>In short, this is what you need for a <code>llms.txt</code> setup.</p>
<table>
<thead>
<tr><th>What</th><th>Description</th></tr>
</thead>
<tbody>
<tr><td><code>/llms.txt</code></td><td>Curated entry point for AI agents</td></tr>
<tr><td><code>/llms-full.txt</code></td><td>Full content expanded in a single document</td></tr>
<tr><td><code>/your-page.md</code></td><td>Clean prose version of each page</td></tr>
<tr><td><code>&lt;link rel=&quot;alternate&quot;&gt;</code> in <code>&lt;head&gt;</code></td><td>Signals a Markdown version is available</td></tr>
<tr><td><code>Link</code> header on root page</td><td>Points crawlers to your <code>llms.txt</code></td></tr>
</tbody>
</table>
<hr />
<h2>Agent Readiness</h2>
<p>While I discovered llms.txt last year, more recently I stumbled upon <a href="https://isitagentready.com" rel="noopener noreferrer external">Is It Agent Ready?</a>, a tool by Cloudflare that scans your site against a growing list of standards. So, it's worth running it to see where you stand.</p>
<p>This platform gives you both results and guidance on how to fix things, so I won't repeat all that here, but here's what I ended up implementing based on its suggestions.</p>
<h3>robots.txt content signals</h3>
<p>Beyond the usual <code>User-agent: *</code> rules, there is now a convention for signaling how you want your content treated by AI crawlers. It's a Cloudflare initiative called <a href="https://contentsignals.org" rel="noopener noreferrer external">Content Signals</a>:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span>User-agent: *</span></span>
<span class="line"><span>Allow: /</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Content-Signal: ai-train=no, search=yes, ai-input=yes</span></span></code></pre><p>The three values are straightforward:</p>
<ul>
<li><code>search</code>: your content can be indexed and shown in search results</li>
<li><code>ai-input</code>: your content can be used as real-time input to AI models, for example when an AI fetches your page to answer a question</li>
<li><code>ai-train</code>: your content can be used to train or fine-tune AI models</li>
</ul>
<p>It's not enforced, but it's a signal. Whether you want to allow training, block it, or say nothing is your call. Being explicit is better than silence.</p>
<p>And fun fact, for this website I actually set <code>ai-input</code> to <code>no</code> 😉.</p>
<h3>API catalog in .well-known</h3>
<p><code>/.well-known/api-catalog</code> is an <a href="https://datatracker.ietf.org/doc/rfc9727/" rel="noopener noreferrer external">IETF standard (RFC 9727)</a> for automated discovery of the APIs a publisher exposes. In Juno's case, there is no public REST API to list, but the format supports linking to service documentation, which I used to point to the AI guide:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">linkset</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#F8F8F2">        {</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">anchor</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">https://juno.build</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">service-doc</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [{ </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">href</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">https://juno.build/docs/guides/ai</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> }]</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    ]</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>I also added a <code>Link</code> response header on the root page pointing to it:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">Link: </span><span style="color:#FF79C6">&#x3C;/</span><span style="color:#F8F8F2">.well</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">known</span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2">api</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">catalog</span><span style="color:#FF79C6">></span><span style="color:#F8F8F2">; rel</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">api-catalog</span><span style="color:#E9F284">"</span></span></code></pre><p>The file needs to be served with <code>Content-Type: application/linkset+json</code>. How you set that depends on your hosting setup.</p>
<hr />
<h2>Skills</h2>
<p><a href="https://agentskills.io" rel="noopener noreferrer external">Agent Skills</a> is another standard, originally created and introduced by Anthropic and since adopted by other platforms like OpenAI, Google Gemini, and GitHub Copilot.</p>
<p>This one is more niche though: you only need it if you have a product or library that developers might build with. If it's just a personal site or blog, you can skip this section.</p>
<h3>Creating your skill</h3>
<p>A skill is a folder containing a <code>SKILL.md</code> file with instructions, scripts, and resources that an AI agent can load dynamically to perform a specific task reliably and repeatedly - think of it as packaged know-how for agents which you typically share as a standalone GitHub repo or subfolder of your project.</p>
<p>For Juno, I created one covering how to develop using its features, SDK, CLI, and how to interact with the platform. You can find it in the <a href="https://github.com/junobuild/skills" rel="noopener noreferrer external">junobuild/skills</a> repository.</p>
<h3>Linking your site</h3>
<p>Once you have the files, you expose them at <code>.well-known/agent-skills/index.json</code> so agents visiting your site can discover them directly.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">$schema</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">https://schemas.agentskills.io/discovery/0.2.0/schema.json</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">skills</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#F8F8F2">        {</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">name</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">juno</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">type</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">skill-md</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">description</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Up-to-date knowledge about Juno's CLI, SDK, and serverless functions for AI coding agents.</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">url</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">https://raw.githubusercontent.com/junobuild/skills/main/SKILL.md</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">digest</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">sha256:18b7fec3e46664b67bdf5bd66f4f36fd9fca138a8749ddc2638f61fed4c23483</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    ]</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>As you can see, it defines the type and URL of the skill but also includes a digest of the <code>SKILL.md</code> file. What I did is recompute the hash on every build through a script and update the file automatically. I also set up a GitHub Action that runs the job once a week and on any PR to the documentation, so it should always stay up-to-date.</p>
<ul>
<li><a href="https://github.com/junobuild/docs/blob/main/scripts/ai.mjs" rel="noopener noreferrer external">Script</a></li>
<li><a href="https://github.com/junobuild/docs/blob/main/.github/workflows/update-ai.yml" rel="noopener noreferrer external">GitHub Action</a></li>
</ul>
<h3>Installation and discoverability</h3>
<p>You should also register them on <a href="https://skills.sh" rel="noopener noreferrer external">skills.sh</a>, a directory created by Vercel for broader discoverability. It also comes with a CLI that makes it easy for anyone to install your skills locally for any AI tool.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npx</span><span style="color:#F1FA8C"> skills</span><span style="color:#F1FA8C"> add</span><span style="color:#F1FA8C"> junobuild/skills</span></span></code></pre><hr />
<h2>Conclusion</h2>
<p>None of this is a silver bullet and you should definitely not just trust me bro. That said, most of it is low-effort to implement once you know these wild wild west conventions exist. It might make your site more useful for LLMs.</p>
<p>Until next time!<br />
David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Sihlwald]]></title>
          <link>https://daviddalbusco.com/trails/2026/sihlwald/</link>
          <pubDate>Wed, 17 Jun 2026 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>So, I bought a gravel...</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Klingenstock - Klingenstock - Fronalpstock]]></title>
          <link>https://daviddalbusco.com/trails/2026/klingenstock-klingenstock-fronalpstock/</link>
          <pubDate>Sat, 13 Jun 2026 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>Seriously, <strong>don't go there on WEEKENDS!</strong></p>
<p>Klingenstock - Fronalpstock is a highway.</p>
<p>Otherwise, pretty nice view and easy run.</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Type-Safe Server-Sent Events Pub/Sub]]></title>
          <description><![CDATA[A typed singleton wrapper around the browser's EventSource API with discriminated union callbacks.]]></description>
          <link>https://daviddalbusco.com/blog/type-safe-server-sent-events-pubsub/</link>
          <pubDate>Fri, 12 Jun 2026 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/richard-horvath-cPccYbPrF-A-unsplash.jpg" alt="" / loading="lazy"></p>
<blockquote>
<p>Photo by <a href="https://unsplash.com/fr/@ricvath?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Richard Horvath</a> on <a href="https://unsplash.com/fr/photos/formes-ondulantes-bleues-et-turquoise-cPccYbPrF-A?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></p>
</blockquote>
<p>Server-Sent Events (<a href="https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events" rel="noopener noreferrer external">SSE</a>) - a browser API for receiving a stream of updates from a server over a single HTTP connection - are simple on the surface. Open a connection, listen for messages, done. The tricky part is when multiple parts of your app subscribe to the same stream but care about different event types, and you want TypeScript to know exactly what each subscriber will receive.</p>
<p>This post walks through how I solved that. To keep things concrete, let's pretend we're building a pizza shop dashboard. 🍕</p>
<hr />
<h2>The problem</h2>
<p>Our dashboard streams real-time updates from the backend: order status changes and delivery tracking events. Both flow through the same SSE endpoint, but a component showing order status shouldn't have to deal with delivery events and vice versa.</p>
<p>The naive solution is to dump everything into one callback and sort it out at the consumer level. It works, but TypeScript can't help you much. Every subscriber gets <code>unknown</code> and you're back to casting.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">eventSource.</span><span style="color:#50FA7B">onmessage</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">event</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> data</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> unknown</span><span style="color:#FF79C6"> =</span><span style="color:#BD93F9"> JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">parse</span><span style="color:#F8F8F2">(event.data);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // Every subscriber gets `unknown`, cast and hope</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> ((data </span><span style="color:#FF79C6">as</span><span style="color:#F8F8F2"> { type</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2"> }).type </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">order</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#50FA7B">        handleOrder</span><span style="color:#F8F8F2">(data </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> OrderEvent</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>What I wanted instead: subscribe with a key that encodes what you care about, and get a callback typed to exactly that payload.</p>
<hr />
<h2>The event key</h2>
<p>The first piece is the event key. Rather than stringing together arbitrary identifiers, I used Zod's <code>z.templateLiteral</code> to define keys that are both validated at runtime and inferred correctly by TypeScript.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { z } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">zod</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> OrderEventKeySchema </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> z.</span><span style="color:#50FA7B">templateLiteral</span><span style="color:#F8F8F2">([</span></span>
<span class="line"><span style="color:#F8F8F2">    z.</span><span style="color:#50FA7B">literal</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">order</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">),</span></span>
<span class="line"><span style="color:#F8F8F2">    z.</span><span style="color:#50FA7B">literal</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">#</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">),</span></span>
<span class="line"><span style="color:#F8F8F2">    z.</span><span style="color:#50FA7B">string</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">uuid</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">]);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> DeliveryEventKeySchema </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> z.</span><span style="color:#50FA7B">templateLiteral</span><span style="color:#F8F8F2">([</span></span>
<span class="line"><span style="color:#F8F8F2">    z.</span><span style="color:#50FA7B">literal</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">delivery</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">),</span></span>
<span class="line"><span style="color:#F8F8F2">    z.</span><span style="color:#50FA7B">literal</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">#</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">),</span></span>
<span class="line"><span style="color:#F8F8F2">    z.</span><span style="color:#50FA7B">string</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">uuid</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">]);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> EventKeySchema </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> z.</span><span style="color:#50FA7B">union</span><span style="color:#F8F8F2">([OrderEventKeySchema, DeliveryEventKeySchema]);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> type</span><span style="color:#8BE9FD;font-style:italic"> EventKey</span><span style="color:#FF79C6"> =</span><span style="color:#8BE9FD;font-style:italic"> z</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">infer</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">typeof</span><span style="color:#F8F8F2"> EventKeySchema>;</span></span>
<span class="line"><span style="color:#6272A4">// "order#&#x3C;uuid>" | "delivery#&#x3C;uuid>"</span></span></code></pre><p>The key is just a string at runtime, but TypeScript and Zod both understand its shape.</p>
<hr />
<h2>The callbacks</h2>
<p>Each event type gets its own payload schema and callback shape.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> OrderEventSchema </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> z.</span><span style="color:#50FA7B">strictObject</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    order_id</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> z.</span><span style="color:#50FA7B">string</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">uuid</span><span style="color:#F8F8F2">(),</span></span>
<span class="line"><span style="color:#F8F8F2">    type</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> z.</span><span style="color:#50FA7B">literal</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">order</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">),</span></span>
<span class="line"><span style="color:#F8F8F2">    status</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> z.</span><span style="color:#50FA7B">enum</span><span style="color:#F8F8F2">([</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">received</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">preparing</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ready</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">picked_up</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">])</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> DeliveryEventSchema </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> z.</span><span style="color:#50FA7B">strictObject</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    delivery_id</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> z.</span><span style="color:#50FA7B">string</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">uuid</span><span style="color:#F8F8F2">(),</span></span>
<span class="line"><span style="color:#F8F8F2">    type</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> z.</span><span style="color:#50FA7B">literal</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">delivery</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">),</span></span>
<span class="line"><span style="color:#F8F8F2">    status</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> z.</span><span style="color:#50FA7B">enum</span><span style="color:#F8F8F2">([</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">on_the_way</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">arrived</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">])</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> OrderCallbackSchema </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> z.</span><span style="color:#50FA7B">strictObject</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    key</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> OrderEventKeySchema,</span></span>
<span class="line"><span style="color:#F8F8F2">    callback</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> z.</span><span style="color:#50FA7B">strictObject</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        onmessage</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> z.</span><span style="color:#50FA7B">function</span><span style="color:#F8F8F2">({ input</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> z.</span><span style="color:#50FA7B">tuple</span><span style="color:#F8F8F2">([OrderEventSchema]) })</span></span>
<span class="line"><span style="color:#F8F8F2">    })</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> DeliveryCallbackSchema </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> z.</span><span style="color:#50FA7B">strictObject</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    key</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> DeliveryEventKeySchema,</span></span>
<span class="line"><span style="color:#F8F8F2">    callback</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> z.</span><span style="color:#50FA7B">strictObject</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        onmessage</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> z.</span><span style="color:#50FA7B">function</span><span style="color:#F8F8F2">({ input</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> z.</span><span style="color:#50FA7B">tuple</span><span style="color:#F8F8F2">([DeliveryEventSchema]) })</span></span>
<span class="line"><span style="color:#F8F8F2">    })</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> EventCallbackSchema </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> z.</span><span style="color:#50FA7B">discriminatedUnion</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">key</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, [</span></span>
<span class="line"><span style="color:#F8F8F2">    OrderCallbackSchema,</span></span>
<span class="line"><span style="color:#F8F8F2">    DeliveryCallbackSchema</span></span>
<span class="line"><span style="color:#F8F8F2">]);</span></span></code></pre><p>The discriminated union on <code>key</code> is what makes it work. When you call <code>subscribe</code> with a key that starts with <code>order#</code>, TypeScript narrows <code>callback.onmessage</code> to only accept <code>OrderEvent</code>. No casting needed.</p>
<hr />
<h2>The stream</h2>
<p>With the types in place, the stream class is mostly plumbing with few things worth watching for that I'll share with you after the code.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">type</span><span style="color:#8BE9FD;font-style:italic"> Unsubscriber</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#8BE9FD;font-style:italic"> void</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">type</span><span style="color:#8BE9FD;font-style:italic"> Result</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> { status</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">success</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">; result</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> T</span><span style="color:#F8F8F2"> } </span><span style="color:#FF79C6">|</span><span style="color:#F8F8F2"> { status</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">error</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">; err</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> unknown</span><span style="color:#F8F8F2"> };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> PizzaStreamAlreadyConnectingError</span><span style="color:#FF79C6"> extends</span><span style="color:#8BE9FD;font-style:italic"> Error</span><span style="color:#F8F8F2"> {}</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> PizzaStreamAlreadyOpenError</span><span style="color:#FF79C6"> extends</span><span style="color:#8BE9FD;font-style:italic"> Error</span><span style="color:#F8F8F2"> {}</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> PizzaStreamAlreadyClosedError</span><span style="color:#FF79C6"> extends</span><span style="color:#8BE9FD;font-style:italic"> Error</span><span style="color:#F8F8F2"> {}</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> PizzaStreamNotInitializedError</span><span style="color:#FF79C6"> extends</span><span style="color:#8BE9FD;font-style:italic"> Error</span><span style="color:#F8F8F2"> {}</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> PizzaStreamAlreadySubscribedError</span><span style="color:#FF79C6"> extends</span><span style="color:#8BE9FD;font-style:italic"> Error</span><span style="color:#F8F8F2"> {}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> STREAM_URL </span><span style="color:#FF79C6">=</span><span style="color:#F1FA8C"> `</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">API_URL</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">/stream`</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> PizzaStream</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    static</span><span style="color:#F8F8F2"> #instance</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> PizzaStream</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    readonly</span><span style="color:#F8F8F2"> #callbacks </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Map</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">EventKey</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">z</span><span style="color:#F8F8F2">.</span><span style="color:#FFB86C;font-style:italic">infer</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">typeof</span><span style="color:#F8F8F2"> EventCallbackSchema>>();</span></span>
<span class="line"><span style="color:#F8F8F2">    #eventSource</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> EventSource</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    private</span><span style="color:#FF79C6"> constructor</span><span style="color:#F8F8F2">() {}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    static</span><span style="color:#50FA7B"> getInstance</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> PizzaStream</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.#instance </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">            this</span><span style="color:#F8F8F2">.#instance </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> PizzaStream</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.#instance;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    open</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Result</span><span style="color:#F8F8F2">&#x3C;{ status</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">initialized</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> }> {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.#eventSource </span><span style="color:#FF79C6">!==</span><span style="color:#BD93F9"> undefined</span><span style="color:#FF79C6"> &#x26;&#x26;</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.#eventSource.readyState </span><span style="color:#FF79C6">!==</span><span style="color:#F8F8F2"> EventSource.</span><span style="color:#BD93F9">CLOSED</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">            switch</span><span style="color:#F8F8F2"> (</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.#eventSource.readyState) {</span></span>
<span class="line"><span style="color:#FF79C6">                case</span><span style="color:#F8F8F2"> EventSource.</span><span style="color:#BD93F9">CONNECTING</span><span style="color:#F8F8F2">:</span></span>
<span class="line"><span style="color:#FF79C6">                    return</span><span style="color:#F8F8F2"> { status</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">error</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, err</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> PizzaStreamAlreadyConnectingError</span><span style="color:#F8F8F2">() };</span></span>
<span class="line"><span style="color:#FF79C6">                default</span><span style="color:#F8F8F2">:</span></span>
<span class="line"><span style="color:#FF79C6">                    return</span><span style="color:#F8F8F2"> { status</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">error</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, err</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> PizzaStreamAlreadyOpenError</span><span style="color:#F8F8F2">() };</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        this</span><span style="color:#F8F8F2">.#eventSource </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> EventSource</span><span style="color:#F8F8F2">(STREAM_URL, { withCredentials</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2"> });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        this</span><span style="color:#F8F8F2">.#eventSource.</span><span style="color:#50FA7B">onerror</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">$event</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#6272A4">            // For demonstration purposes only. In production, consider notifying the consumer or reconnecting on failure.</span></span>
<span class="line"><span style="color:#F8F8F2">            console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Unexpected error received while streaming:</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, $event);</span></span>
<span class="line"><span style="color:#F8F8F2">        };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">        // If the consumer needs to await the channel being open,</span></span>
<span class="line"><span style="color:#6272A4">        // use this callback and transform the function into a promise.</span></span>
<span class="line"><span style="color:#6272A4">        // this.#eventSource.onopen = ($event) => {};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        this</span><span style="color:#F8F8F2">.#eventSource.</span><span style="color:#50FA7B">addEventListener</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">pizza-update</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.#dispatch);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> { status</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">success</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, result</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { status</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">initialized</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> } };</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    close</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Result</span><span style="color:#F8F8F2">&#x3C;{ status</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">closed</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> }> {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.#eventSource </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#F8F8F2"> { status</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">error</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, err</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> PizzaStreamNotInitializedError</span><span style="color:#F8F8F2">() };</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.#eventSource.readyState </span><span style="color:#FF79C6">===</span><span style="color:#F8F8F2"> EventSource.</span><span style="color:#BD93F9">CLOSED</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#F8F8F2"> { status</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">error</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, err</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> PizzaStreamAlreadyClosedError</span><span style="color:#F8F8F2">() };</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        this</span><span style="color:#F8F8F2">.#eventSource.</span><span style="color:#50FA7B">close</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> { status</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">success</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, result</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { status</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">closed</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> } };</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // Help TypeScript infer callback types</span></span>
<span class="line"><span style="color:#50FA7B">    subscribe</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">params</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { callback</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> z</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">infer</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">typeof</span><span style="color:#F8F8F2"> OrderCallbackSchema> })</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Result</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Unsubscriber</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#50FA7B">    subscribe</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">params</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { callback</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> z</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">infer</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">typeof</span><span style="color:#F8F8F2"> DeliveryCallbackSchema> })</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Result</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Unsubscriber</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#50FA7B">    subscribe</span><span style="color:#F8F8F2">({ </span><span style="color:#FFB86C;font-style:italic">callback</span><span style="color:#F8F8F2"> }</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { callback</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> z</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">infer</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">typeof</span><span style="color:#F8F8F2"> EventCallbackSchema> })</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Result</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Unsubscriber</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> { key } </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> callback;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.#callbacks.</span><span style="color:#50FA7B">has</span><span style="color:#F8F8F2">(key)) {</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">                status</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">error</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">                err</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> PizzaStreamAlreadySubscribedError</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Callback already subscribed.</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, { cause</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> key })</span></span>
<span class="line"><span style="color:#F8F8F2">            };</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        this</span><span style="color:#F8F8F2">.#callbacks.</span><span style="color:#50FA7B">set</span><span style="color:#F8F8F2">(key, callback);</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> { status</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">success</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#50FA7B">result</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.#callbacks.</span><span style="color:#50FA7B">delete</span><span style="color:#F8F8F2">(key) };</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    #dispatch</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">$event</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> MessageEvent</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> unknown</span><span style="color:#F8F8F2">>) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#6272A4">        // The backend sends each message as a 2-element tuple</span></span>
<span class="line"><span style="color:#6272A4">        // [orderEvents[], deliveryEvents[]], in that fixed order (either may be empty)</span></span>
<span class="line"><span style="color:#6272A4">        // Which we represent with a schema for validation.</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> PizzaEventSchema </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> z.</span><span style="color:#50FA7B">tuple</span><span style="color:#F8F8F2">([z.</span><span style="color:#50FA7B">array</span><span style="color:#F8F8F2">(OrderEventSchema), z.</span><span style="color:#50FA7B">array</span><span style="color:#F8F8F2">(DeliveryEventSchema)]);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> parsed </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#F8F8F2">            $event.data </span><span style="color:#FF79C6">!==</span><span style="color:#BD93F9"> undefined</span><span style="color:#FF79C6"> &#x26;&#x26;</span><span style="color:#FF79C6"> typeof</span><span style="color:#F8F8F2"> $event.data </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">string</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#FF79C6">                ?</span><span style="color:#F8F8F2"> PizzaEventSchema.</span><span style="color:#50FA7B">safeParse</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">parse</span><span style="color:#F8F8F2">($event.data))</span></span>
<span class="line"><span style="color:#FF79C6">                :</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (parsed?.success </span><span style="color:#FF79C6">!==</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> { data } </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> parsed;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> [orderEvents, deliveryEvents] </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> data;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        for</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> orderEvent </span><span style="color:#FF79C6">of</span><span style="color:#F8F8F2"> orderEvents) {</span></span>
<span class="line"><span style="color:#FF79C6">            const</span><span style="color:#F8F8F2"> key</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> EventKey</span><span style="color:#FF79C6"> =</span><span style="color:#F1FA8C"> `order#</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">orderEvent.order_id</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">            const</span><span style="color:#F8F8F2"> entry </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.#callbacks.</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(key) </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> z</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">infer</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">typeof</span><span style="color:#F8F8F2"> OrderCallbackSchema> </span><span style="color:#FF79C6">|</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">            entry?.callback.</span><span style="color:#50FA7B">onmessage</span><span style="color:#F8F8F2">(orderEvent);</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        for</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> deliveryEvent </span><span style="color:#FF79C6">of</span><span style="color:#F8F8F2"> deliveryEvents) {</span></span>
<span class="line"><span style="color:#FF79C6">            const</span><span style="color:#F8F8F2"> key</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> EventKey</span><span style="color:#FF79C6"> =</span><span style="color:#F1FA8C"> `delivery#</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">deliveryEvent.delivery_id</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">            const</span><span style="color:#F8F8F2"> entry </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.#callbacks.</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(key) </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> z</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">infer</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">typeof</span><span style="color:#F8F8F2"> DeliveryCallbackSchema> </span><span style="color:#FF79C6">|</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">            entry?.callback.</span><span style="color:#50FA7B">onmessage</span><span style="color:#F8F8F2">(deliveryEvent);</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>I used a singleton to keep one connection per app, not one per component. Components subscribe and unsubscribe, but the underlying <code>EventSource</code> is shared. The goal is to avoid opening one connection per component and overloading the backend. It's not a guarantee, but it's a good-faith effort on the client side.</p>
<p>The <code>subscribe</code> overloads are how TypeScript knows which callback type to expect. The implementation signature accepts the union, but callers get the specific type based on what they pass in.</p>
<p><code>Result&lt;T&gt;</code> instead of throwing makes error handling explicit at the call site. The stream either worked or it didn't, and the caller decides what to do.</p>
<p>Before acting, <code>open()</code> and <code>close()</code> check the connection's <code>readyState</code>. Calling <code>open()</code> twice, for example, returns a typed error instead of silently doing nothing.</p>
<p>Finally, rather than looking up a single event, <code>#dispatch</code> loops over arrays of order and delivery events, since a single SSE message can batch multiple updates of each type.</p>
<hr />
<h2>Using it in the UI</h2>
<p>Rather than wiring up the subscription in every component that needs it, I created a wrapper component that handles the lifecycle and passes the data down to its children. The example below is for orders, but delivery would look fairly similar.</p>
<blockquote>
<p>I used Svelte here, but the same idea applies in any framework. The code I shared above is agnostic.</p>
</blockquote>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#50FA7B;font-style:italic"> lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    import </span><span style="color:#FF79C6">{</span><span style="color:#F8F8F2"> onDestroy, type Snippet </span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2"> from "svelte";</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    interface Props </span><span style="color:#FF79C6">{</span></span>
<span class="line"><span style="color:#F8F8F2">        orderId: string </span><span style="color:#FF79C6">|</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        children: Snippet;</span></span>
<span class="line"><span style="color:#FF79C6">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    let </span><span style="color:#FF79C6">{</span><span style="color:#F8F8F2"> orderId, children </span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">: Props = $props();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    let unsubscriber = $state&#x3C;</span><span style="color:#8BE9FD;font-style:italic">Unsubscriber</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">|</span><span style="color:#50FA7B;font-style:italic"> undefined</span><span style="color:#F8F8F2">>(undefined);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    $effect(() => </span><span style="color:#FF79C6">{</span></span>
<span class="line"><span style="color:#50FA7B">        if</span><span style="color:#F8F8F2"> (orderId </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">            unsubscriber?.();</span></span>
<span class="line"><span style="color:#F8F8F2">            return;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        const subscribed </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> PizzaStream.</span><span style="color:#50FA7B">getInstance</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">subscribe</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">            callback</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">                key</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> `order#</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">orderId</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">                callback</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">                    onmessage</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">event</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">                        console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Order status:</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, event.status);</span></span>
<span class="line"><span style="color:#F8F8F2">                    }</span></span>
<span class="line"><span style="color:#F8F8F2">                }</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">        if</span><span style="color:#F8F8F2"> (subscribed.status </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">error</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#6272A4">            // A subscription error here indicates a code usage issue, not a runtime failure.</span></span>
<span class="line"><span style="color:#F8F8F2">            console.warn(subscribed.err);</span></span>
<span class="line"><span style="color:#F8F8F2">            return;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        unsubscriber </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> subscribed.result;</span></span>
<span class="line"><span style="color:#FF79C6">    }</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    onDestroy(() => </span><span style="color:#FF79C6">{</span></span>
<span class="line"><span style="color:#50FA7B">        unsubscriber</span><span style="color:#F8F8F2">?.();</span></span>
<span class="line"><span style="color:#FF79C6">    }</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">{</span><span style="color:#F8F8F2">@render </span><span style="color:#50FA7B">children</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">}</span></span></code></pre><p>And that covers it.</p>
<hr />
<h2>Conclusion</h2>
<p>The pattern scales well. Adding a new event type means adding a new schema, a new callback type, and a new overload. But the bigger takeaway is the single streamer for the entire app. Any component can subscribe to what it cares about, unsubscribe when it's done, and share the same underlying connection. No coordination needed, no duplicated setup.</p>
<p>Until next time!<br />
David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Schwyz - Rigi - Immensee]]></title>
          <link>https://daviddalbusco.com/trails/2026/schwyz-rigi-immensee/</link>
          <pubDate>Mon, 08 Jun 2026 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>One of my classic pre-alpine trails.</p>
<p>Pro-tips: run it during the week as I did this year. It was really enjoyable, not crowded as it can be on weekends.</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[A Self-Hostable Wasm Sandbox for JavaScript Workers]]></title>
          <description><![CDATA[A walkthrough of the CLI I built to run JavaScript workers on a VPS without Node.js, Bun, or Docker.]]></description>
          <link>https://daviddalbusco.com/blog/a-self-hostable-wasm-sandbox-for-javascript-workers/</link>
          <pubDate>Fri, 05 Jun 2026 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/li-zhang-K-DwbsTXliY-unsplash.jpg" alt="" / loading="lazy"></p>
<blockquote>
<p>Photo by <a href="https://unsplash.com/fr/@sunx?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Li Zhang</a> on <a href="https://unsplash.com/fr/photos/un-fond-abstrait-rouge-et-orange-avec-des-courbes-K-DwbsTXliY?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></p>
</blockquote>
<p>I've been building <a href="https://github.com/junobuild/juno" rel="noopener noreferrer external">Juno</a> for a while, a platform where apps run in some sort of containers. For its authentication, I also built an API with Bun that runs on a VPS. So, needless to say, I like running my own sandboxes.</p>
<p>When I recently tried Cloudflare Workers to build my <a href="https://github.com/peterpeterparker/blog-to-newsletter-action" rel="noopener noreferrer external">&quot;blog to newsletter&quot;</a> action pipeline, something clicked: a single function, sandboxed, stateless, handling HTTP... maybe there is something to experiment here?</p>
<p>I started wondering: could I actually build something similar that runs on a VPS without Node.js, Bun, or even Docker installed on the host?</p>
<p>That experiment is <strong><a href="https://kyushu.dev" rel="noopener noreferrer external">Kyushu</a></strong>.</p>
<hr />
<h2>Overview</h2>
<p>I already knew JavaScript could run inside Wasm (Juno does exactly that). What I wanted to prove was whether I could build a Wasm binary that runs a JS handler and pair it with an engine that handles HTTP and forwards requests into it.</p>
<p>Turns out both were possible and that's how Kyushu ended up with two parts: a <strong>worker</strong> and a <strong>runner</strong>.</p>
<p>The worker is a Wasm binary that runs your JavaScript. The runner (<code>kyu run</code>) is a Rust binary that loads the worker and handles HTTP. That's it.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">┌─────────────────────────────────────────┐</span></span>
<span class="line"><span style="color:#F8F8F2">│                kyu run                  │</span></span>
<span class="line"><span style="color:#F8F8F2">│                                         │</span></span>
<span class="line"><span style="color:#F8F8F2">│  ┌──────────────────────────────────┐   │</span></span>
<span class="line"><span style="color:#F8F8F2">│  │         </span><span style="color:#50FA7B">Wasmtime</span><span style="color:#F8F8F2"> (host)          │   │</span></span>
<span class="line"><span style="color:#F8F8F2">│  │                                  │   │</span></span>
<span class="line"><span style="color:#F8F8F2">│  │  ┌────────────────────────────┐  │   │</span></span>
<span class="line"><span style="color:#F8F8F2">│  │  │   worker.</span><span style="color:#50FA7B">wasm</span><span style="color:#F8F8F2"> (sandbox)    │  │   │</span></span>
<span class="line"><span style="color:#F8F8F2">│  │  │                            │  │   │</span></span>
<span class="line"><span style="color:#F8F8F2">│  │  │  QuickJS </span><span style="color:#FF79C6">+</span><span style="color:#F8F8F2"> your JS code    │  │   │</span></span>
<span class="line"><span style="color:#F8F8F2">│  │  └────────────────────────────┘  │   │</span></span>
<span class="line"><span style="color:#F8F8F2">│  └──────────────────────────────────┘   │</span></span>
<span class="line"><span style="color:#F8F8F2">└─────────────────────────────────────────┘</span></span>
<span class="line"><span style="color:#F8F8F2">         ▲                    │</span></span>
<span class="line"><span style="color:#F8F8F2">    HTTP request         HTTP response</span></span></code></pre><p>Let's look at each part.</p>
<hr />
<h2>The worker</h2>
<p>The core of Kyushu is a WebAssembly component targeting <code>wasm32-wasip2</code>. It embeds <a href="https://bellard.org/quickjs/" rel="noopener noreferrer external">QuickJS</a> - a small, embeddable JavaScript engine - via <a href="https://github.com/DelSkayn/rquickjs" rel="noopener noreferrer external">rquickjs</a>, a Rust crate that provides safe bindings.</p>
<p>It receives an HTTP request, passes it to a JavaScript handler, and returns a response. The JavaScript itself lives inside the binary too, but we'll get to that.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">┌─────────────────────────────────────────┐</span></span>
<span class="line"><span style="color:#F8F8F2">│            worker.</span><span style="color:#50FA7B">wasm</span><span style="color:#F8F8F2"> (sandbox)        │</span></span>
<span class="line"><span style="color:#F8F8F2">│                                         │</span></span>
<span class="line"><span style="color:#F8F8F2">│  HTTP request                           │</span></span>
<span class="line"><span style="color:#F8F8F2">│       │                                 │</span></span>
<span class="line"><span style="color:#F8F8F2">│       ▼                                 │</span></span>
<span class="line"><span style="color:#F8F8F2">│  [ JsRequest ]                          │</span></span>
<span class="line"><span style="color:#F8F8F2">│       │                                 │</span></span>
<span class="line"><span style="color:#F8F8F2">│       ▼                                 │</span></span>
<span class="line"><span style="color:#F8F8F2">│  [ QuickJS </span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2"> your JS code ]             │</span></span>
<span class="line"><span style="color:#F8F8F2">│       │                                 │</span></span>
<span class="line"><span style="color:#F8F8F2">│       ▼                                 │</span></span>
<span class="line"><span style="color:#F8F8F2">│  [ JsResponse ]                         │</span></span>
<span class="line"><span style="color:#F8F8F2">│       │                                 │</span></span>
<span class="line"><span style="color:#F8F8F2">│       ▼                                 │</span></span>
<span class="line"><span style="color:#F8F8F2">│  HTTP response                          │</span></span>
<span class="line"><span style="color:#F8F8F2">└─────────────────────────────────────────┘</span></span></code></pre><p>The worker implements the <code>wasi:http/incoming-handler</code> interface from the <a href="https://github.com/WebAssembly/wasi-http" rel="noopener noreferrer external">WASI HTTP</a> spec. When a request comes in, it gets deserialized into a <code>JsRequest</code> struct and passed to QuickJS as a plain JavaScript object. The handler runs, returns a <code>JsResponse</code>, and the worker serializes it back out.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">pub</span><span style="color:#FF79C6"> struct</span><span style="color:#8BE9FD;font-style:italic"> JsRequest</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    pub</span><span style="color:#F8F8F2"> method</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HttpMethod</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#FF79C6">    pub</span><span style="color:#F8F8F2"> url</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> String</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#FF79C6">    pub</span><span style="color:#F8F8F2"> headers</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Option</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">Vec</span><span style="color:#F8F8F2">&#x3C;(</span><span style="color:#8BE9FD;font-style:italic">String</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">String</span><span style="color:#F8F8F2">)>>,</span></span>
<span class="line"><span style="color:#FF79C6">    pub</span><span style="color:#F8F8F2"> body</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Option</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">Body</span><span style="color:#F8F8F2">>,</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">impl</span><span style="color:#F8F8F2">&#x3C;'</span><span style="color:#8BE9FD;font-style:italic">js</span><span style="color:#F8F8F2">> </span><span style="color:#8BE9FD;font-style:italic">IntoJs</span><span style="color:#F8F8F2">&#x3C;'</span><span style="color:#8BE9FD;font-style:italic">js</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">for</span><span style="color:#8BE9FD;font-style:italic"> JsRequest</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    fn</span><span style="color:#50FA7B"> into_js</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9;font-style:italic">self</span><span style="color:#F8F8F2">, ctx</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#8BE9FD;font-style:italic">Ctx</span><span style="color:#F8F8F2">&#x3C;'</span><span style="color:#8BE9FD;font-style:italic">js</span><span style="color:#F8F8F2">>) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> JsResult</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">Value</span><span style="color:#F8F8F2">&#x3C;'</span><span style="color:#8BE9FD;font-style:italic">js</span><span style="color:#F8F8F2">>> {</span></span>
<span class="line"><span style="color:#FF79C6">        let</span><span style="color:#F8F8F2"> obj </span><span style="color:#FF79C6">=</span><span style="color:#8BE9FD;font-style:italic"> Object</span><span style="color:#FF79C6">::</span><span style="color:#50FA7B">new</span><span style="color:#F8F8F2">(ctx</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">clone</span><span style="color:#F8F8F2">())</span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">        // Serialize each field as a JS object property</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">        Ok</span><span style="color:#F8F8F2">(obj</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">into_value</span><span style="color:#F8F8F2">())</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">#[derive(</span><span style="color:#8BE9FD;font-style:italic">Debug</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">Clone</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">PartialEq</span><span style="color:#F8F8F2">)]</span></span>
<span class="line"><span style="color:#FF79C6">pub</span><span style="color:#FF79C6"> enum</span><span style="color:#8BE9FD;font-style:italic"> Body</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    Text</span><span style="color:#F8F8F2">(</span><span style="color:#8BE9FD;font-style:italic">String</span><span style="color:#F8F8F2">),</span></span>
<span class="line"><span style="color:#50FA7B">    Bytes</span><span style="color:#F8F8F2">(</span><span style="color:#8BE9FD;font-style:italic">Vec</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">u8</span><span style="color:#F8F8F2">>),</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">pub</span><span style="color:#FF79C6"> struct</span><span style="color:#8BE9FD;font-style:italic"> JsResponse</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    pub</span><span style="color:#F8F8F2"> status</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> u16</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#FF79C6">    pub</span><span style="color:#F8F8F2"> body</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Option</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">Body</span><span style="color:#F8F8F2">>,</span></span>
<span class="line"><span style="color:#FF79C6">    pub</span><span style="color:#F8F8F2"> headers</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Vec</span><span style="color:#F8F8F2">&#x3C;(</span><span style="color:#8BE9FD;font-style:italic">String</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">String</span><span style="color:#F8F8F2">)>,</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">impl</span><span style="color:#F8F8F2">&#x3C;'</span><span style="color:#8BE9FD;font-style:italic">js</span><span style="color:#F8F8F2">> </span><span style="color:#8BE9FD;font-style:italic">FromJs</span><span style="color:#F8F8F2">&#x3C;'</span><span style="color:#8BE9FD;font-style:italic">js</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">for</span><span style="color:#8BE9FD;font-style:italic"> JsResponse</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    fn</span><span style="color:#50FA7B"> from_js</span><span style="color:#F8F8F2">(_ctx</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#8BE9FD;font-style:italic">Ctx</span><span style="color:#F8F8F2">&#x3C;'</span><span style="color:#8BE9FD;font-style:italic">js</span><span style="color:#F8F8F2">>, value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Value</span><span style="color:#F8F8F2">&#x3C;'</span><span style="color:#8BE9FD;font-style:italic">js</span><span style="color:#F8F8F2">>) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> JsResult</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#BD93F9;font-style:italic">Self</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">        let</span><span style="color:#F8F8F2"> obj </span><span style="color:#FF79C6">=</span><span style="color:#8BE9FD;font-style:italic"> Object</span><span style="color:#FF79C6">::</span><span style="color:#50FA7B">from_value</span><span style="color:#F8F8F2">(value)</span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">        // Deserialize each field from the JS object returned by the handler</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">        Ok</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9;font-style:italic">Self</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">            status,</span></span>
<span class="line"><span style="color:#F8F8F2">            body,</span></span>
<span class="line"><span style="color:#F8F8F2">            headers,</span></span>
<span class="line"><span style="color:#F8F8F2">        })</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>The <code>Body</code> type handles both text and binary, mapping to <code>string | ArrayBuffer | Uint8Array</code> on the JavaScript side.</p>
<p>One thing worth mentioning: the worker also uses the <a href="https://github.com/nicolo-ribaudo/wasm-rquickjs" rel="noopener noreferrer external">wasm-rquickjs</a> polyfills, which provide Node.js-compatible APIs inside the Wasm sandbox - things like <code>fs</code>, <code>path</code>, <code>crypto</code>, and more. This means a surprisingly large amount of npm packages just work.</p>
<hr />
<h2>The runner</h2>
<p>Once you have a worker artifact, <code>kyu run</code> is what actually serves it. It loads the <code>.wasm</code> file, starts an HTTP server using <a href="https://hyper.rs" rel="noopener noreferrer external">hyper</a>, and forwards incoming requests into the Wasm sandbox via <a href="https://wasmtime.io" rel="noopener noreferrer external">Wasmtime</a>.</p>
<p>Wasmtime is the WebAssembly runtime. It handles the WASI interface implementation - filesystem access, environment variables, stdio - and wires up the <code>wasi:http/incoming-handler</code> interface that the worker implements.</p>
<p>The sandbox is configured via <code>kyushu.run.toml</code>. Filesystem mounts are explicit - nothing is accessible by default, likewise for the environment variables. The worker can only see what you tell it to see.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">[</span><span style="color:#8BE9FD">worker</span><span style="color:#FF79C6">]</span></span>
<span class="line"><span style="color:#8BE9FD">wasm</span><span style="color:#FF79C6"> =</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">dist/__kyushu_worker.wasm</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#8BE9FD">port</span><span style="color:#FF79C6"> =</span><span style="color:#BD93F9"> 5987</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">[[</span><span style="color:#8BE9FD">mounts</span><span style="color:#FF79C6">]]</span></span>
<span class="line"><span style="color:#8BE9FD">host</span><span style="color:#FF79C6"> =</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./public</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#8BE9FD">guest</span><span style="color:#FF79C6"> =</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">/public</span><span style="color:#E9F284">"</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">[[</span><span style="color:#8BE9FD">env</span><span style="color:#FF79C6">]]</span></span>
<span class="line"><span style="color:#8BE9FD">key</span><span style="color:#FF79C6"> =</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">API_KEY</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#8BE9FD">value</span><span style="color:#FF79C6"> =</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">secret</span><span style="color:#E9F284">"</span></span></code></pre><p>The request flow looks like this:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">HTTP request</span></span>
<span class="line"><span style="color:#F8F8F2">  └── </span><span style="color:#50FA7B">hyper</span><span style="color:#F8F8F2"> (host)</span></span>
<span class="line"><span style="color:#F8F8F2">  └── Wasmtime</span></span>
<span class="line"><span style="color:#F8F8F2">        └── wasi:http</span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2">incoming</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">handler</span></span>
<span class="line"><span style="color:#F8F8F2">        └── worker.wasm</span></span>
<span class="line"><span style="color:#F8F8F2">              └── QuickJS</span></span>
<span class="line"><span style="color:#F8F8F2">              └── your </span><span style="color:#50FA7B">fetch</span><span style="color:#F8F8F2">() handler</span></span>
<span class="line"><span style="color:#F8F8F2">              └── JsResponse</span></span>
<span class="line"><span style="color:#F8F8F2">  └── HTTP response</span></span></code></pre><hr />
<h2>The build step</h2>
<p>At this point, you might be wondering how the JavaScript ends up inside the <code>.wasm</code> file. That's what <code>kyu build</code> is for.</p>
<p>When you run this command, two things happen.</p>
<p>First, your TypeScript or JavaScript entry point gets bundled using <a href="https://rolldown.rs" rel="noopener noreferrer external">Rolldown</a>.</p>
<p>Second, and this is the interesting part, the bundle gets pre-initialized into the worker using <a href="https://github.com/bytecodealliance/wizer" rel="noopener noreferrer external">Wizer</a>.</p>
<p>Wizer is a tool that runs a Wasm module up to a defined initialization point, then snapshots the resulting memory state into a new Wasm binary. In Kyushu's case, that means executing the worker's initialization code - setting up the QuickJS runtime, loading the polyfills, evaluating the JavaScript bundle - and then freezing that state.</p>
<p>The resulting <code>.wasm</code> file has your code baked in, the module already evaluated and initialized. When a request arrives, there's no startup overhead. The worker just picks up from where Wizer left off, which likely means a negligible cold start (I still need to do some benchmarking).</p>
<p>Moreover, and this is a part I'm really excited at too, the worker binary ships with the CLI and you never need to install cargo or recompile anything on your end. <code>kyu build</code> is just bundling and snapshotting. Makes the tooling lightweight and portable.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">kyu build</span></span>
<span class="line"><span style="color:#F8F8F2">  └── rolldown bundles src</span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2">index.ts → bundle.mjs</span></span>
<span class="line"><span style="color:#F8F8F2">  └── wizer injects bundle into worker.wasm</span></span>
<span class="line"><span style="color:#F8F8F2">        └── QuickJS initializes</span></span>
<span class="line"><span style="color:#F8F8F2">        └── polyfills load</span></span>
<span class="line"><span style="color:#F8F8F2">        └── your </span><span style="color:#FF79C6">module</span><span style="color:#8BE9FD;font-style:italic"> evaluates</span></span>
<span class="line"><span style="color:#F8F8F2">        └── </span><span style="color:#8BE9FD;font-style:italic">snapshot</span><span style="color:#F8F8F2"> → </span><span style="color:#8BE9FD;font-style:italic">dist</span><span style="color:#F8F8F2">/</span><span style="color:#8BE9FD;font-style:italic">__kyushu_worker</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">wasm</span></span></code></pre><hr />
<h2>The types</h2>
<p>To provide a state of the art JS dev experience, I also ship a small npm package: <a href="https://www.npmjs.com/package/kyushu-types" rel="noopener noreferrer external">kyushu-types</a>.</p>
<p>It provides TypeScript interfaces for <code>WorkerRequest</code>, <code>WorkerResponse</code>, and <code>ExportedHandler</code>, along with <a href="https://zod.dev" rel="noopener noreferrer external">Zod</a> schemas for runtime validation. The worker validates both the incoming request and the outgoing response at the Wasm boundary, so if your handler returns something unexpected, you get a clear error rather than a silent failure.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#FF79C6"> type</span><span style="color:#F8F8F2"> { ExportedHandler } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">kyushu-types</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    async</span><span style="color:#50FA7B"> fetch</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">request</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">            status</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 200</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">            headers</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">content-type</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">application/json</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> },</span></span>
<span class="line"><span style="color:#F8F8F2">            body</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">stringify</span><span style="color:#F8F8F2">({ hello</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">world</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> })</span></span>
<span class="line"><span style="color:#F8F8F2">        };</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">} </span><span style="color:#FF79C6">satisfies</span><span style="color:#8BE9FD;font-style:italic"> ExportedHandler</span><span style="color:#F8F8F2">;</span></span></code></pre><hr />
<h2>Shipping it</h2>
<p>The worker and CLI have separate release pipelines.</p>
<p>When a new worker version is tagged, a GitHub Action builds the <code>.wasm</code> component, publishes it as a GitHub Release artifact, and automatically opens a PR that updates the binary committed in the CLI. That way the worker binary is always versioned and the CLI picks it up on the next CLI release.</p>
<p>For the CLI itself, I used <a href="https://opensource.axo.dev/cargo-dist/" rel="noopener noreferrer external">cargo-dist</a>. It generates GitHub Actions workflows that build cross-platform binaries, publish them as GitHub Releases, and produce a shell installer script per release.</p>
<hr />
<h2>The meta bit</h2>
<p>The only thing missing was a stable URL that always points to the latest installer. So I needed to set up an alias, that's what <code>kyushu.dev/install</code> is for.</p>
<p>This route serves the cargo-dist shell installer script. So when you run:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">curl</span><span style="color:#BD93F9"> -fsSL</span><span style="color:#F1FA8C"> https://kyushu.dev/install</span><span style="color:#FF79C6"> |</span><span style="color:#50FA7B"> bash</span></span></code></pre><p>You're downloading a file served by a Kyushu worker, running on a VPS.</p>
<blockquote>
<p>I used <a href="https://www.hetzner.com/" rel="noopener noreferrer external">Hetzner</a> here.</p>
</blockquote>
<p>But to have this, I also needed a website.</p>
<hr />
<h2>Website</h2>
<p>For the documentation (obviously at this point I also had to build one), I developed a <a href="https://starlight.astro.build" rel="noopener noreferrer external">Starlight</a> site, but with a twist: what better dogfooding opportunity than serving kyushu.dev with... Kyushu?</p>
<p>So I wrote a static file server as a Kyushu worker. It reads files from a mounted directory and handles MIME types, brotli compression, HEAD requests, and path aliases. Check out the <a href="https://github.com/peterpeterparker/kyushu/blob/main/packages/docs/src/server.ts" rel="noopener noreferrer external">source on GitHub</a> for the full implementation.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#FF79C6"> type</span><span style="color:#F8F8F2"> { ExportedHandler } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">kyushu-types</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { readFile, access } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">node:fs/promises</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { join } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">node:path</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> mime </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">mime-types</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    async</span><span style="color:#50FA7B"> fetch</span><span style="color:#F8F8F2">({ url: </span><span style="color:#FFB86C;font-style:italic">requestUrl</span><span style="color:#F8F8F2"> }) {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> url </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> URL</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">parse</span><span style="color:#F8F8F2">(requestUrl);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (url </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> null</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#F8F8F2"> { status</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 400</span><span style="color:#F8F8F2">, body</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Bad Request</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> };</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> { pathname } </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> url;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> sanitizedPathname </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> pathname </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">/</span><span style="color:#E9F284">"</span><span style="color:#FF79C6"> ?</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">/index.html</span><span style="color:#E9F284">"</span><span style="color:#FF79C6"> :</span><span style="color:#F8F8F2"> pathname;</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> filepath </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> join</span><span style="color:#F8F8F2">(process.</span><span style="color:#50FA7B">cwd</span><span style="color:#F8F8F2">(), </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">public</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, sanitizedPathname);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">            await</span><span style="color:#50FA7B"> access</span><span style="color:#F8F8F2">(filepath);</span></span>
<span class="line"><span style="color:#F8F8F2">        } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#F8F8F2"> { status</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 404</span><span style="color:#F8F8F2">, body</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Not Found</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> };</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">            const</span><span style="color:#F8F8F2"> file </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> readFile</span><span style="color:#F8F8F2">(filepath);</span></span>
<span class="line"><span style="color:#FF79C6">            const</span><span style="color:#F8F8F2"> mimeType </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> mime.</span><span style="color:#50FA7B">lookup</span><span style="color:#F8F8F2">(filepath);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">                status</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 200</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">                headers</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#E9F284">                    "</span><span style="color:#F1FA8C">content-type</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> typeof</span><span style="color:#F8F8F2"> mimeType </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">string</span><span style="color:#E9F284">"</span><span style="color:#FF79C6"> ?</span><span style="color:#F8F8F2"> mimeType </span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">application/octet-stream</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">                },</span></span>
<span class="line"><span style="color:#F8F8F2">                body</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> file</span></span>
<span class="line"><span style="color:#F8F8F2">            };</span></span>
<span class="line"><span style="color:#F8F8F2">        } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#F8F8F2"> { status</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 500</span><span style="color:#F8F8F2">, body</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Internal Server Error</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> };</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">} </span><span style="color:#FF79C6">satisfies</span><span style="color:#8BE9FD;font-style:italic"> ExportedHandler</span><span style="color:#F8F8F2">;</span></span></code></pre><p>Is that meta enough? When you read the documentation or install Kyushu you are actually using Kyushu 🤪.</p>
<hr />
<h2>What's next</h2>
<p><a href="https://kyushu.dev" rel="noopener noreferrer external">Kyushu</a> is early-stage so not exactly sure what's next. I might use it to build other projects and move away from the experiment stage, or just leave it as is. It will likely depend on the reception of the project, who knows, you might have read these lines and give it a try.</p>
<p>The source is on <a href="https://github.com/peterpeterparker/kyushu" rel="noopener noreferrer external">GitHub</a>. Let me know what you think!</p>
<p>Until next time!<br />
David</p>
<blockquote>
<p><strong>Update:</strong> I already converted my <a href="https://github.com/peterpeterparker/blog-to-newsletter-worker" rel="noopener noreferrer external">blog-to-newsletter</a> pipeline, replacing the Cloudflare Worker with a Kyushu. 😅</p>
</blockquote>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Automating My Newsletter with Claude, Telegram, and a Cloudflare Worker]]></title>
          <description><![CDATA[How I built a GitHub Action that reads a new blog post, generates a newsletter draft, and delivers it to subscribers after an approval tap.]]></description>
          <link>https://daviddalbusco.com/blog/automating-newsletter-claude-telegram-cloudflare-worker/</link>
          <pubDate>Tue, 02 Jun 2026 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/maxim-berg-TcE45yIzJA0-unsplash.jpg" alt="" / loading="lazy"></p>
<blockquote>
<p>Photo by <a href="https://unsplash.com/fr/@maxberg?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Maxim Berg</a> on <a href="https://unsplash.com/fr/photos/motif-darriere-plan-TcE45yIzJA0?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></p>
</blockquote>
<p>Every time I publish a blog post, I want my newsletter subscribers to hear about it. The problem is I kept forgetting to actually send it and when I did remember, navigating Mailchimp's bloated UI felt like punishment. So I decided to automate the whole thing, but with a human-in-the-loop step before anything goes out.</p>
<p>The result is two open-source projects:</p>
<ul>
<li><a href="https://github.com/peterpeterparker/blog-to-newsletter-action" rel="noopener noreferrer external">blog-to-newsletter-action</a>: a GitHub Action that detects a new post, generates a newsletter with Claude, creates a Mailchimp draft, and pings me on Telegram for approval</li>
<li><a href="https://github.com/peterpeterparker/blog-to-newsletter-worker" rel="noopener noreferrer external">blog-to-newsletter-worker</a>: a Cloudflare Worker that receives the Telegram button taps and acts on them</li>
</ul>
<hr />
<h2>How it works</h2>
<p>When a PR that adds a new blog post is merged to <code>main</code>, the action kicks in:</p>
<ol>
<li>It calls the GitHub API to find which <code>.md</code> files were added in that commit</li>
<li>Reads the content of each post</li>
<li>Sends everything to Claude with a prompt asking for a short, punchy newsletter</li>
<li>Creates a draft campaign in Mailchimp</li>
<li>Sends me a Telegram message with the subject, preview text, and three buttons: Send Test Email, Discard, and Approve &amp; Send</li>
</ol>
<p>Tapping a button hits the Cloudflare Worker, which calls the appropriate Mailchimp endpoint and edits the Telegram message to confirm.</p>
<p><img src="https://daviddalbusco.com/assets/images/github-action-claude-telegram-cloudflare-worker.png" alt="Sequence diagram of the newsletter automation pipeline" / loading="lazy"></p>
<hr />
<h2>Usage</h2>
<p>In practice, here's how I set up the workflow on my blog:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#8BE9FD">name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> Newsletter</span></span>
<span class="line"></span>
<span class="line"><span style="color:#BD93F9">on</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">  push</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">    branches</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#F1FA8C">main</span><span style="color:#F8F8F2">]</span></span>
<span class="line"><span style="color:#8BE9FD">    paths</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#FF79C6">      -</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">src/blog/**</span><span style="color:#E9F284">"</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">jobs</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">  newsletter</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">    runs-on</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> ubuntu-latest</span></span>
<span class="line"><span style="color:#8BE9FD">    steps</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#FF79C6">      -</span><span style="color:#8BE9FD"> name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> Checkout</span></span>
<span class="line"><span style="color:#8BE9FD">        uses</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd</span><span style="color:#6272A4"> # v6.0.2</span></span>
<span class="line"><span style="color:#8BE9FD">        with</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">          persist-credentials</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> false</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">      -</span><span style="color:#8BE9FD"> uses</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> peterpeterparker/blog-to-newsletter-action@main</span></span>
<span class="line"><span style="color:#8BE9FD">        with</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">          blog_posts_path</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">src/blog</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#8BE9FD">          blog_base_url</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">https://daviddalbusco.com</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#8BE9FD">          blog_author</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">David</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#8BE9FD">          github_token</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> ${{ secrets.GITHUB_TOKEN }}</span></span>
<span class="line"><span style="color:#8BE9FD">          mailchimp_api_key</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> ${{ secrets.MAILCHIMP_API_KEY }}</span></span>
<span class="line"><span style="color:#8BE9FD">          mailchimp_reply_to</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">hi@daviddalbusco.com</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#8BE9FD">          mailchimp_list_id</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">123456</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#8BE9FD">          mailchimp_test_emails</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">hi@daviddalbusco.com</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#8BE9FD">          anthropic_api_key</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> ${{ secrets.ANTHROPIC_API_KEY }}</span></span>
<span class="line"><span style="color:#8BE9FD">          telegram_bot_token</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> ${{ secrets.TELEGRAM_BOT_TOKEN }}</span></span>
<span class="line"><span style="color:#8BE9FD">          telegram_chat_id</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> ${{ secrets.TELEGRAM_CHAT_ID }}</span></span></code></pre><hr />
<h2>The infrastructure</h2>
<p>The GitHub Action runs as a Docker container that uses Bun as its runtime. It pulls the image from Docker Hub when the workflow runs. The action is also listed on the <a href="https://github.com/marketplace/actions/blog-to-newsletter" rel="noopener noreferrer external">GitHub Marketplace</a> so it is easy to discover and reference by version.</p>
<p>The Cloudflare Worker handles the Telegram webhook callbacks. My first idea was to run this handler as a Docker container on a VPS alongside the action, but given how rarely I send newsletters, the cost of maintaining a server for something that fires maybe once a month felt hard to justify hence picking a free tier that fits.</p>
<p>The action can be used out of the box with some configuration as for the worker, it has to be forked and deployed by yourself.</p>
<hr />
<h2>The architecture</h2>
<p>Both projects are written in TypeScript and designed to be extensible. Mailchimp, Claude, and Telegram are the current providers, but swapping any of them out should be straightforward. I also built the pipeline to be reusable beyond blog posts. One can imagine that something is committed and needs a boring task handled by AI but needs a human review before being pushed out to consumers.</p>
<p>Each service is a class decorated with a provider interface (<code>@NewsletterProvider</code>, <code>@AIProvider</code>, etc.). The decorator enforces that the class implements the expected interface and exposes a static <code>create()</code> method, making the boundaries explicit and the implementation swappable.</p>
<blockquote>
<p>Fun fact: I could have used abstract classes instead, but abstract classes in TypeScript cannot enforce static methods. A decorator on a concrete class is the cleanest way to enforce both the instance interface and the static factory method at the same time.</p>
</blockquote>
<p>Each <code>create()</code> method validates required env vars on instantiation, throwing immediately if anything is missing. For everything else, no function throws but always returns a result.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B;font-style:italic">NewsletterProvider</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> Mailchimp</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    readonly</span><span style="color:#F8F8F2"> #apiKey</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    private</span><span style="color:#FF79C6"> constructor</span><span style="color:#F8F8F2">({ </span><span style="color:#FFB86C;font-style:italic">apiKey</span><span style="color:#F8F8F2"> }</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { apiKey</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2"> }) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        this</span><span style="color:#F8F8F2">.#apiKey </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> apiKey;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    static</span><span style="color:#50FA7B"> create</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Mailchimp</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> { MAILCHIMP_API_KEY } </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> process.env;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">        assertNotEmptyString</span><span style="color:#F8F8F2">(MAILCHIMP_API_KEY, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">MAILCHIMP_API_KEY</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">({ apiKey</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> MAILCHIMP_API_KEY });</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    async</span><span style="color:#50FA7B"> generateNewsletter</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">blog</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Blog</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Result</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">NewsletterPayload</span><span style="color:#F8F8F2">>> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> messageResult </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">#createMessage</span><span style="color:#F8F8F2">(blog);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (messageResult.status </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">error</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#F8F8F2"> messageResult;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> { result</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> messageResponse } </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> messageResult;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">#buildNewsletter</span><span style="color:#F8F8F2">(messageResponse);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>Security</h3>
<p>A few small things worth calling out.</p>
<ol>
<li>The Cloudflare Worker only accepts POST requests to <code>/telegram/&lt;TELEGRAM_SECRET&gt;</code>, where the secret is a random 32-character string set as a Worker secret. Anything hitting the wrong path gets a 403.</li>
<li>The Telegram bot token and Mailchimp API key are stored as Worker secrets and never exposed in code or logs.</li>
<li>On the action side, the <code>actions/checkout</code> step uses a pinned commit SHA and <code>persist-credentials: false</code>. The action itself is also pinned by SHA in the caller workflow, so a new release never silently changes behavior.</li>
</ol>
<h3>Lean dependencies</h3>
<p>Given how many npm supply chain attacks have made the news lately, I kept the dependency footprint small on purpose. Both projects share a single runtime dependency: <code>zod</code>. Everything else is built on standard Web APIs and Bun built-ins.</p>
<hr />
<h2>Interesting gotchas</h2>
<p>A few things that came up during the development that I thought were worth sharing.</p>
<h3>Detecting added files via the GitHub API</h3>
<p>My first instinct was to use <code>git diff</code> inside the Docker container. It just turned out not to work: the workspace is mounted by GitHub Actions and owned by root, but the container runs as the <code>bun</code> user, so git kept throwing permission errors on <code>.git/FETCH_HEAD</code>, <code>.git/shallow</code>, and a few other files it needed to write to. Some might argue that I could have just run the app as root of course but, that's IMO a bad practice.</p>
<p>I eventually switched to the GitHub API instead. The commits endpoint returns exactly what I need, the list of files added in a commit, and it requires no filesystem access at all:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> response </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> fetch</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`https://api.github.com/repos/</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">repository</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">/commits/</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">sha</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">, {</span></span>
<span class="line"><span style="color:#F8F8F2">    headers</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { Authorization</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> `Bearer </span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">token</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2"> }</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><p>Much simpler, zero permission issues.</p>
<h3>Prompting Claude for a newsletter</h3>
<p>The prompt is extracted into a <code>prompt.md</code> file and imported as text using Bun's native text import:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> prompt </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./_prompt.md</span><span style="color:#E9F284">"</span><span style="color:#FF79C6"> with</span><span style="color:#F8F8F2"> { type</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">text</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> };</span></span></code></pre><p>The file uses <code>{{author}}</code>, <code>{{audience}}</code>, and <code>{{posts}}</code> as placeholders, replaced at runtime. Having the prompt in a Markdown file makes it much easier to iterate on without touching the code. Moreover, if the action ever gets more widely used, it would also make it straightforward for consumers to swap in their own prompt entirely.</p>
<p>The key constraint in the prompt though is handling the respond only with valid JSON, no markdown fences, no preamble.</p>
<p>Despite that, Claude occasionally wraps the response in <code>```json</code> fences anyway. So I strip them before parsing:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> raw </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> content</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">join</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">""</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">replace</span><span style="color:#F8F8F2">(</span><span style="color:#FF5555">/</span><span style="color:#FF79C6">^</span><span style="color:#F1FA8C">```json</span><span style="color:#BD93F9">\s</span><span style="color:#FF79C6">*</span><span style="color:#FF5555">/</span><span style="color:#FF79C6">i</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">""</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">replace</span><span style="color:#F8F8F2">(</span><span style="color:#FF5555">/</span><span style="color:#FF79C6">^</span><span style="color:#F1FA8C">```</span><span style="color:#BD93F9">\s</span><span style="color:#FF79C6">*</span><span style="color:#FF5555">/</span><span style="color:#FF79C6">i</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">""</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">replace</span><span style="color:#F8F8F2">(</span><span style="color:#FF5555">/</span><span style="color:#F1FA8C">```</span><span style="color:#BD93F9">\s</span><span style="color:#FF79C6">*$</span><span style="color:#FF5555">/</span><span style="color:#FF79C6">i</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">""</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">trim</span><span style="color:#F8F8F2">();</span></span></code></pre><h3>Keeping fetch call sites readable with Zod codecs</h3>
<p>Instead of inlining <code>JSON.stringify</code> and <code>JSON.parse</code> directly in every <code>fetch</code> call, I used Zod codecs to handle serialization, deserialization, and validation in one place.</p>
<p>For example:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> AnthropicMessageCodec </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> z.</span><span style="color:#50FA7B">codec</span><span style="color:#F8F8F2">(AnthropicMessageSchema, z.</span><span style="color:#50FA7B">string</span><span style="color:#F8F8F2">(), {</span></span>
<span class="line"><span style="color:#50FA7B">    decode</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">model</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">maxTokens</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">content</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span></span>
<span class="line"><span style="color:#BD93F9">        JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">stringify</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">            model,</span></span>
<span class="line"><span style="color:#F8F8F2">            max_tokens</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> maxTokens,</span></span>
<span class="line"><span style="color:#F8F8F2">            messages</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [{ role</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">user</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, content }]</span></span>
<span class="line"><span style="color:#F8F8F2">        }),</span></span>
<span class="line"><span style="color:#50FA7B">    encode</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">json</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#BD93F9"> JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">parse</span><span style="color:#F8F8F2">(json)</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><p>Then the fetch call site just becomes:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">body: AnthropicMessageCodec.</span><span style="color:#50FA7B">decode</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    model</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.#model,</span></span>
<span class="line"><span style="color:#F8F8F2">    maxTokens</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.#maxTokens,</span></span>
<span class="line"><span style="color:#F8F8F2">    content</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><p>The fetch call stays focused on the request logic. The codec owns the wire format and validates the input and output.</p>
<h3>Embedding test emails in the callback data</h3>
<p>The Telegram approval message optionally includes a Send Test Email button. That button needs to know which email addresses to send to, but the Cloudflare Worker has no access to the action's environment, by design. I did not want consumers to have to configure the same emails in two places, so the Worker only sees what Telegram sends it.</p>
<p>The solution: embed the emails directly in the button's <code>callback_data</code>:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">test:campaignId:email1@example.com,email2@example.com</span></span></code></pre><p>The Worker parses the callback data, extracts the emails, validates them with Zod, and calls the Mailchimp test endpoint. No shared state, no extra secrets in the Worker.</p>
<h3>Not editing the message after a test email</h3>
<p>After sending a test email, I originally called <code>editMessage</code> to append a confirmation line. That removed the Approve and Discard buttons from the chat, which meant I was stuck with no more actions. So I just dropped the <code>editMessage</code> call for the test action. When you tap &quot;Send Test Email&quot; you are supposed to get an email anyway, that's your feedback.</p>
<hr />
<h2>Conclusion</h2>
<p>If you publish a blog and send newsletters, feel free to fork <a href="https://github.com/peterpeterparker/blog-to-newsletter-worker" rel="noopener noreferrer external">blog-to-newsletter-worker</a> and drop <a href="https://github.com/peterpeterparker/blog-to-newsletter-action" rel="noopener noreferrer external">blog-to-newsletter-action</a> into your repo. I'll be using it for &quot;real&quot; with this very first post.</p>
<p>Until next time!<br />
David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Weesen - Walenstadt - Murg]]></title>
          <link>https://daviddalbusco.com/trails/2026/weesen-walenstadt-murg/</link>
          <pubDate>Sun, 31 May 2026 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>Same trail, another year. 🏃‍♂️⛰️</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Lake Yamanaka (with Fuji View)]]></title>
          <link>https://daviddalbusco.com/trails/2026/lake-yamanaka-with-fuji-view/</link>
          <pubDate>Wed, 20 May 2026 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>The first 10 km and last km run on concrete alongside a fairly busy road, but the rest is almost entirely through forest and it's worth it: it leads to nice little summits with a view on the Fuji.</p>
<p>I barely crossed paths with anyone on the trail, just two people 6-8 km deep in the forest, so you should have it mostly to yourself and be comfortable with it. I wore my bear bell, though I'm not sure if bears actually roam the area.</p>
<p>A heads-up: the first few kilometres into the forest are not very well signed.</p>
<p>I originally found the route on AllTrails (https://www.alltrails.com/fr/randonnee/japan/yamanashi/lake-yamanaka-outer-rim-loop) but tweaked it to avoid starting next to the road. The trailhead is easily reachable by bus.</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Mount Yake]]></title>
          <link>https://daviddalbusco.com/trails/2026/mount-yake/</link>
          <pubDate>Sun, 17 May 2026 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>⚠️ DO NOT FOLLOW THIS ITINERARY ⚠️</p>
<p>Good, now that you are warned.</p>
<p>Found this route on AllTrails but I wouldn't follow it as-is. A few km in you're forced onto the road and through two tunnels used by cars and buses. Not fun.</p>
<p>That said, the highlights are worth it. Mount Yake was stunning, with snow still on the path in mid-May. Tip: head up early and you'll naturally follow other hikers with crampons.</p>
<p>After descending I attempted Nishihotakaguchi but turned back due to snow and low energy.</p>
<p>TL;DR: Target Mount Yake and Nishihotakaguchi, but plan your own route to avoid the road. Mid-May means expect snow, bring crampons or go with the crowd.</p>
<hr />
<p>AllTrails sources: https://www.alltrails.com/trail/japan/nagano/mount-yakedake-kamikochi and https://www.alltrails.com/fr/randonnee/japan/nagano/nishi-hotakaguchi-nishiho-sanso-kamikoch</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Mount Kujū]]></title>
          <link>https://daviddalbusco.com/trails/2026/mount-kuju/</link>
          <pubDate>Sun, 10 May 2026 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>A loop around mounts Taisen (1,786m), Nakadake (1,791m) and Kujū (1,788m), all part of the 100 famous Japanese mountains.</p>
<p>The itinerary starts with a loop around Taisen, which I'm not sure I would ever do again. The route is basically tons of rocks and bushes, so I got a few bruises and also hit my head a few times in the woods, because unless you are 1m50 tall, you need to pay attention to both feet and head. The path wasn't always straightforward either.</p>
<p>The other mountains have easier routes, though the ascent is a bit technical.</p>
<p>I rented a car and started at the free parking lot, which happens to be full around 7:30am, so get up early if you want to use it. There are a few other parking lots around, but I'm not sure if they are accessible or tied to hotels.</p>
<p>I originally found the itinerary on https://www.alltrails.com/trail/japan/oita/kuju-mountains-chojabaru-loop</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Osaka - Mount Ikoma]]></title>
          <link>https://daviddalbusco.com/trails/2026/osaka-mount-ikoma/</link>
          <pubDate>Sun, 03 May 2026 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>A loop around mounts Taisen (1,786m), Nakadake (1,791m) and Kujū (1,788m), all part of the 100 famous Japanese mountains.</p>
<p>The itinerary starts with a loop around Taisen, which I'm not sure I would ever do again. The route is basically tons of rocks and bushes, so I got a few bruises and also hit my head a few times in the woods, because unless you are 1m50 tall, you need to pay attention to both feet and head. The path wasn't always straightforward either.</p>
<p>The other mountains have easier routes, though the ascent is a bit technical.</p>
<p>I rented a car and started at the free parking lot, which happens to be full around 7:30am, so get up early if you want to use it. There are a few other parking lots around, but I'm not sure if they are accessible or tied to hotels.</p>
<p>I originally found the itinerary on https://www.alltrails.com/trail/japan/oita/kuju-mountains-chojabaru-loop</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Up (2x) and around Uetliberg]]></title>
          <link>https://daviddalbusco.com/trails/2026/up-2x-and-around-uetliberg/</link>
          <pubDate>Fri, 24 Apr 2026 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>One of my traditional 'distance' training routes. Ran it quickly before heading off on holidays ;)</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Serving Local Assets with Bun]]></title>
          <description><![CDATA[A minimal Bun script to serve static files locally without extra dependencies.]]></description>
          <link>https://daviddalbusco.com/blog/serving-local-assets-with-bun/</link>
          <pubDate>Fri, 17 Apr 2026 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/logan-voss-1QlMVjKbJrY-unsplash.jpg" alt="" / loading="lazy"></p>
<blockquote>
<p>Photo by <a href="https://unsplash.com/fr/@loganvoss?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Logan Voss</a> on <a href="https://unsplash.com/fr/photos/lignes-et-graphiques-abstraits-aux-teintes-bleues-et-roses-1QlMVjKbJrY?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></p>
</blockquote>
<p>I don't always have to write about a super niche topic, I guess. So, I thought about sharing a small helper I've developed for this website.</p>
<p>I'm hosting it on <a href="https://juno.build" rel="noopener noreferrer external">Juno</a>, the full-stack platform I built to deploy and run apps in WASM containers with zero DevOps. I do so in a bit of an unconventional way because I like to have many different variants of what's possible with my tooling. This way it's not solely tested with a large automated test base but also IRL.</p>
<p>For this particular use case, I deviated from the standard way of deploying by uploading its assets using <code>juno run</code>, a command that allows running arbitrary scripts in the context of the Juno CLI or GitHub Actions.</p>
<p>I do so because after crossposting my posts for a long time, I switched to having my website as the canonical source. That meant collecting all the images I had been using, since I didn't want to rely on any external sources. Rather than dropping them into the SvelteKit static folder, I decided to handle them as pure static assets. Given the number of images, it just felt like it would keep the build and dev experience smoother.</p>
<p>That's possible and even easy to do because my container can serve web storage. I just had to write a <a href="https://github.com/peterpeterparker/daviddalbusco.com/blob/main/juno.upload.assets.ts" rel="noopener noreferrer external">little script</a>.</p>
<p>Everything worked fine, except locally as I kept running into a chicken-and-egg issue when writing new articles: the new images I was using weren't available until I deployed them, but I couldn't deploy them until my post was wrapped up.</p>
<p>Long story short, I needed to serve them locally.</p>
<p>For that, one option would have been <code>npx serve</code>, but I try to avoid anything Vercel. I could have searched for a handy npm package, but that's always one more dependency. I could have written a quick Node script (gosh, so many options nowadays 😄), but lately I've been having fun with Bun, which is already installed on my machine and provides a <a href="https://bun.com/docs/runtime/http/server" rel="noopener noreferrer external">high-performance server</a>. So I went with this last solution.</p>
<hr />
<h2>Serve Assets with Bun</h2>
<p>Here's the little script I've set up:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { join } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">node:path</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> BASE_PATH </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./assets</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> PORT </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> 3000</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">Bun.</span><span style="color:#50FA7B">serve</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    port</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> PORT,</span></span>
<span class="line"><span style="color:#FF79C6">    async</span><span style="color:#50FA7B"> fetch</span><span style="color:#F8F8F2">({ </span><span style="color:#FFB86C;font-style:italic">url</span><span style="color:#F8F8F2"> }) {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> { pathname } </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> URL</span><span style="color:#F8F8F2">(url);</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> filePath </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> join</span><span style="color:#F8F8F2">(BASE_PATH, pathname);</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> file </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> Bun.</span><span style="color:#50FA7B">file</span><span style="color:#F8F8F2">(filePath);</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Response</span><span style="color:#F8F8F2">(file);</span></span>
<span class="line"><span style="color:#F8F8F2">    },</span></span>
<span class="line"><span style="color:#50FA7B">    error</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">error</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Response</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`Error: </span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">error.message</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">, { status</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 404</span><span style="color:#F8F8F2"> });</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`Assets served from </span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">BASE_PATH</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C"> on http://localhost:</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">PORT</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">);</span></span></code></pre><p>It's pretty minimal:</p>
<ul>
<li><code>fetch</code> handles every incoming request</li>
<li>No routing is needed, which was actually the &quot;trickiest&quot; part to figure out since most Bun examples show named routes.</li>
<li>It resolves the request using the URL <code>pathname</code> against the <code>./assets</code> folder</li>
<li>Reads the file with <code>Bun.file</code></li>
<li>And streams it back as a <code>Response</code>.</li>
</ul>
<p>If anything goes wrong (file not found, bad path), the <code>error</code> handler catches it all and returns a <code>404</code>.</p>
<p>That's it. No rocket science, just Bun doing what it does well.</p>
<p>Until next time!<br />
David</p>
<hr />
<p>PS: for this to work locally, if someone is interested in implementing a similar setup, I actually also set a <code>PUBLIC_ASSETS</code> environment variable pointing to <code>http://localhost:3000</code> and replace the image URLs in my markdown SSG generator accordingly.</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Building a GitHub Actions Integration with OIDC Authentication]]></title>
          <description><![CDATA[How I replaced secrets with short-lived tokens for deploying with GitHub Actions.]]></description>
          <link>https://daviddalbusco.com/blog/building-a-github-actions-integration-with-oidc-authentication/</link>
          <pubDate>Mon, 23 Feb 2026 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/anatoliy-shostak-MzEB7zNqXCo-unsplash.jpg" alt="" / loading="lazy"></p>
<blockquote>
<p>Photo by <a href="https://unsplash.com/fr/@photogolic?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Anatoliy Shostak</a> on <a href="https://unsplash.com/fr/photos/un-fond-bleu-fonce-avec-des-feux-dartifice-rouges-et-blancs-MzEB7zNqXCo?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></p>
</blockquote>
<hr />
<p>I rarely take the time to step back after releasing a new feature on <a href="https://juno.build/" rel="noopener noreferrer external">Juno</a>. I generally build, build, build, ship, and repeat. On top of that, writing long blog posts often feels like a relative waste of time, as they rarely find their audience. Nonetheless, for once, I thought I'd take a moment to write about what I released recently: deploying frontends or publishing serverless functions from GitHub Actions using ephemeral access keys granted through an OpenID Connect flow.</p>
<p>The following is a walkthrough of the solution. I'll go through the various steps required to implement such a workflow and highlight the pieces I feel are important.</p>
<p>It won't be a tutorial, nor something you or your AI can — or should — copy-paste to implement a similar outcome. But hopefully it contains something interesting — or at least leads someone to reach out for suggestions of improvements.</p>
<hr />
<h2>Introduction</h2>
<p>Authenticating CI/CD pipelines without long-lived secrets is not a new concept and, even though I kind of randomly learned about it, GitHub Actions has been <a href="https://docs.github.com/en/actions/concepts/security/openid-connect" rel="noopener noreferrer external">supporting OpenID Connect</a> for a while now.</p>
<p>The idea is simple: instead of storing a secret that can leak, rotate, or be forgotten, the CI generates a short-lived token — a JWT that proves <em>where</em> the workflow is running: which repository, which branch, which actor.</p>
<p>The twist is that the other end is not a typical server. In Juno, the developer's application is in a Rust WASM container (which I call a Satellite) running on the Internet Computer. Since the underlying infrastructure is a public blockchain, sensitive information has to be handled carefully — secrets cannot simply be stored and trusted the way they would on a traditional server.</p>
<p>That's what makes the implementation a bit particular, and why the flow ultimately goes through quite a few steps and assertions.</p>
<p>To give you an idea, the diagram below summarizes the flow, from the moment a GitHub Actions workflow is triggered to the moment the ephemeral access key is approved for use.</p>
<p><img src="https://daviddalbusco.com/assets/images/github-actions-with-oidc-authentication-flow.png" alt="Sequence diagram showing the full GitHub Actions OIDC authentication flow, from workflow trigger to ephemeral access key approval" / loading="lazy"></p>
<p>Before any deployment happens, the Actions workflow generates an identity — a key-pair, a nonce, and their corresponding salt. It then exchanges the nonce with GitHub's API to obtain a signed JWT that also contains information about the repository, actor, and workflow.</p>
<p>This JWT, along with the salt, is sent to the satellite using the generated identity. An identity is essentially a private-public key pair.</p>
<p>On the satellite side, the JWT header is decoded first to identify the provider (&quot;is it GitHub calling?&quot;), then the public keys are retrieved — either from cache or from Observatory, Juno's public infrastructure, which runs a cronjob to keep those keys fresh.</p>
<p>From there, the satellite runs a series of verifications: the JWK signature and timestamps, the salt to prevent man-in-the-middle attacks, the claims to confirm the request comes from the configured repository, and finally the JTI to prevent replay attacks.</p>
<p>If everything passes, the identity is saved as an allowed access key and the workflow can proceed with the deployment.</p>
<p>From there it's standard Juno usage — the GitHub Action uploads a file, and the access key is validated to approve or reject the operation. I won't cover that part here, there are already enough steps as it is 😅.</p>
<p>Alright, ready? There are quite a few moving parts here, so buckle up, the following chapters will go through each piece in detail.</p>
<hr />
<h2>The GitHub Action</h2>
<p>The entry point of the integration is a bash script (<a href="https://github.com/junobuild/juno-action/blob/main/entrypoint.sh" rel="noopener noreferrer external">source</a>). Nothing fancy, it checks which command is being run, decides whether authentication is needed, and if so, generates an ephemeral token before passing it to the CLI.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#BD93F9">JUNO_TOKEN</span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2">$(</span><span style="color:#50FA7B">node</span><span style="color:#F1FA8C"> /kit/token/src/authenticate.ts</span><span style="color:#E9F284"> "</span><span style="color:#FFB86C;font-style:italic">$@</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#8BE9FD">echo</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">::add-mask::</span><span style="color:#BD93F9">$JUNO_TOKEN</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#BD93F9"> JUNO_TOKEN</span></span></code></pre><p>Two things worth noting here:</p>
<ol>
<li>The generated token is passed as <code>JUNO_TOKEN</code>, the same environment variable the CLI already supports. No changes were needed in the CLI itself, which was a nice side effect of the approach.</li>
<li><code>::add-mask::</code> ensures the token never appears in the Actions logs, even accidentally. We do not want to leak the private key in some logs...</li>
</ol>
<p>From there, a custom script within the action takes over. I like abstraction, so rather than implementing the authentication flow directly, it delegates to <code>@junobuild/auth</code> — a library I maintain — through a single function call: <code>authenticateAutomation</code> (<a href="https://github.com/junobuild/juno-action/blob/main/kit/token/src/_authenticate.ts" rel="noopener noreferrer external">source</a>).</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> { identity } </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> authenticateAutomation</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    github</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        credentials</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">            generateJwt</span></span>
<span class="line"><span style="color:#F8F8F2">        },</span></span>
<span class="line"><span style="color:#F8F8F2">        automation</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">            satellite</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">                container,</span></span>
<span class="line"><span style="color:#F8F8F2">                satelliteId</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><p>The library doesn't know anything about GitHub. It just knows it needs a JWT and will ask for one by calling the <code>generateJwt</code> callback. This means the action is responsible for implementing that callback: taking the nonce, calling GitHub's <a href="https://docs.github.com/en/actions/reference/security/oidc#methods-for-requesting-the-oidc-token" rel="noopener noreferrer external">token endpoint</a>, and returning the signed JWT.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> generateJwt</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">nonce</span><span style="color:#F8F8F2"> }</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { nonce</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> response </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> fetch</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">process.env.</span><span style="color:#BD93F9">ACTIONS_ID_TOKEN_REQUEST_URL</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">&#x26;audience=</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">nonce</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">, {</span></span>
<span class="line"><span style="color:#F8F8F2">        headers</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">            Authorization</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> `Bearer </span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">process.env.</span><span style="color:#BD93F9">ACTIONS_ID_TOKEN_REQUEST_TOKEN</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">            Accept</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">application/json; api-version=2.0</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">            "</span><span style="color:#F1FA8C">Content-Type</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">application/json</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">response.ok) {</span></span>
<span class="line"><span style="color:#FF79C6">        throw</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Error</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Could not retrieve token</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { value</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> oidcToken } </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> response.</span><span style="color:#50FA7B">json</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> { jwt</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> oidcToken };</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>You may notice <code>audience=${nonce}</code> in the URL. This is a workaround: GitHub does not allow customizing any fields of the JWT aside from the <code>aud</code> (audience) claim. So I use that field, which is not really meant for this purpose, to pass the nonce along.</p>
<p><code>process.env.ACTIONS_ID_TOKEN_REQUEST_URL</code> and <code>process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN</code> are environment variables that GitHub automatically injects into the workflow, but only if the workflow declares <code>id-token: write</code> permissions.</p>
<p>That <code>nonce</code> passed to the callback is generated by the library itself before calling back, and it is not purely random. It is derived from a SHA-256 hash of a salt and the identity's principal (basically a public key), encoded as a base64 URL string (<a href="https://github.com/junobuild/juno-js/blob/main/packages/auth/src/utils/nonce.utils.ts" rel="noopener noreferrer external">source</a>).</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> generateSalt</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Salt</span><span style="color:#FF79C6"> =></span><span style="color:#F8F8F2"> crypto.</span><span style="color:#50FA7B">getRandomValues</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6;font-weight:bold">new</span><span style="color:#50FA7B"> Uint8Array</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">32</span><span style="color:#F8F8F2">));</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> buildNonce</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">salt</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">caller</span><span style="color:#F8F8F2"> }</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { salt</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Salt</span><span style="color:#F8F8F2">; caller</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Ed25519KeyIdentity</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> principal </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> caller.</span><span style="color:#50FA7B">getPrincipal</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">toUint8Array</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> bytes </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Uint8Array</span><span style="color:#F8F8F2">(salt.length </span><span style="color:#FF79C6">+</span><span style="color:#F8F8F2"> principal.byteLength);</span></span>
<span class="line"><span style="color:#F8F8F2">    bytes.</span><span style="color:#50FA7B">set</span><span style="color:#F8F8F2">(salt);</span></span>
<span class="line"><span style="color:#F8F8F2">    bytes.</span><span style="color:#50FA7B">set</span><span style="color:#F8F8F2">(principal, salt.length);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> hash </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> crypto.subtle.</span><span style="color:#50FA7B">digest</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">SHA-256</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, bytes);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#50FA7B"> toBase64URL</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">arrayBufferToUint8Array</span><span style="color:#F8F8F2">(hash));</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>This matters because the satellite will later reconstruct the nonce from the same inputs and verify it matches, which is what prevents man-in-the-middle attacks.</p>
<blockquote>
<p>Note that <code>caller</code> is our identity, our access key. I use the three terms interchangeably in this post.</p>
</blockquote>
<p>Once the JWT is obtained, it is sent to the satellite along with the salt, using the generated identity as the caller (<a href="https://github.com/junobuild/juno-js/blob/main/packages/auth/src/automation/_automation.ts" rel="noopener noreferrer external">source</a>).</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> result </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> authenticateAutomationApi</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    args</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        OpenId</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">            jwt,</span></span>
<span class="line"><span style="color:#F8F8F2">            salt</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    },</span></span>
<span class="line"><span style="color:#F8F8F2">    actorParams</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        automation,</span></span>
<span class="line"><span style="color:#F8F8F2">        identity</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> caller</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><p>If the satellite approves, the identity is returned as the ephemeral access key, serialized and passed back to the CLI as <code>JUNO_TOKEN</code>.</p>
<p>Later on, a cleanup step, registered as a trap on exit of the entry script, takes care of removing the access key from the satellite once the workflow is done (regardless of whether it succeeded or failed).</p>
<hr />
<h2>The Satellite</h2>
<p>On the Rust side, the entry point is <code>openid_prepare_automation</code> (<a href="https://github.com/junobuild/juno/blob/main/src/libs/satellite/src/automation/prepare.rs" rel="noopener noreferrer external">source</a>). It receives the JWT and the salt sent by the action, and kicks off the verification chain.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">pub</span><span style="color:#FF79C6"> async</span><span style="color:#FF79C6"> fn</span><span style="color:#50FA7B"> openid_prepare_automation</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#F8F8F2">    args</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#8BE9FD;font-style:italic">OpenIdPrepareAutomationArgs</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    providers</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#8BE9FD;font-style:italic">OpenIdAutomationProviders</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> OpenIdPrepareAutomationResult</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> (credential, provider) </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#FF79C6">        match</span><span style="color:#F8F8F2"> credentials</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">automation</span><span style="color:#FF79C6">::</span><span style="color:#50FA7B">verify_openid_credentials_with_jwks_renewal</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#FF79C6">            &#x26;</span><span style="color:#F8F8F2">args</span><span style="color:#FF79C6">.</span><span style="color:#F8F8F2">jwt, </span><span style="color:#FF79C6">&#x26;</span><span style="color:#F8F8F2">args</span><span style="color:#FF79C6">.</span><span style="color:#F8F8F2">salt, providers, </span><span style="color:#FF79C6">&#x26;</span><span style="color:#8BE9FD;font-style:italic">AuthHeap</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        )</span></span>
<span class="line"><span style="color:#FF79C6">        .await</span></span>
<span class="line"><span style="color:#F8F8F2">        {</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">            Ok</span><span style="color:#F8F8F2">(value) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> value,</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">            Err</span><span style="color:#F8F8F2">(err) </span><span style="color:#FF79C6">=></span><span style="color:#FF79C6"> return</span><span style="color:#8BE9FD;font-style:italic"> Err</span><span style="color:#F8F8F2">(</span><span style="color:#8BE9FD;font-style:italic">PrepareAutomationError</span><span style="color:#FF79C6">::</span><span style="color:#50FA7B">from</span><span style="color:#F8F8F2">(err)),</span></span>
<span class="line"><span style="color:#F8F8F2">        };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> result </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> automation</span><span style="color:#FF79C6">::</span><span style="color:#50FA7B">openid_prepare_automation</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">&#x26;</span><span style="color:#F8F8F2">provider, </span><span style="color:#FF79C6">&#x26;</span><span style="color:#8BE9FD;font-style:italic">AuthHeap</span><span style="color:#F8F8F2">, </span><span style="color:#FF79C6">&#x26;</span><span style="color:#8BE9FD;font-style:italic">AuthAutomation</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    result</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">map</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">|</span><span style="color:#F8F8F2">prepared_automation</span><span style="color:#FF79C6">|</span><span style="color:#F8F8F2"> (prepared_automation, provider, credential))</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>The first thing <code>verify_openid_credentials_with_jwks_renewal</code> (<a href="https://github.com/junobuild/juno/blob/main/src/libs/auth/src/openid/credentials/automation/verify.rs" rel="noopener noreferrer external">source</a>) does is figure out who is calling. It decodes the JWT header without verifying the signature, just enough to read the issuer (<code>iss</code>) which identifies who generated the token (&quot;is this GitHub? Google? Something else?&quot;) and matches it against the configured providers.</p>
<p>This is safe because the signature verification happens right after — skipping it here is just a pragmatic shortcut to avoid fetching the wrong set of public keys. (<a href="https://github.com/junobuild/juno/blob/main/src/libs/auth/src/openid/jwt/provider.rs" rel="noopener noreferrer external">source</a>)</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">pub</span><span style="color:#FF79C6"> fn</span><span style="color:#50FA7B"> unsafe_find_jwt_provider</span><span style="color:#F8F8F2">&#x3C;'</span><span style="color:#8BE9FD;font-style:italic">a</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">Provider</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">Config</span><span style="color:#F8F8F2">>(</span></span>
<span class="line"><span style="color:#F8F8F2">    providers</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#F8F8F2">'</span><span style="color:#8BE9FD;font-style:italic">a</span><span style="color:#8BE9FD;font-style:italic"> BTreeMap</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">Provider</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">Config</span><span style="color:#F8F8F2">>,</span></span>
<span class="line"><span style="color:#F8F8F2">    jwt</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#8BE9FD;font-style:italic">str</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> Result</span><span style="color:#F8F8F2">&#x3C;(</span><span style="color:#8BE9FD;font-style:italic">Provider</span><span style="color:#F8F8F2">, </span><span style="color:#FF79C6">&#x26;</span><span style="color:#F8F8F2">'</span><span style="color:#8BE9FD;font-style:italic">a</span><span style="color:#8BE9FD;font-style:italic"> Config</span><span style="color:#F8F8F2">), </span><span style="color:#8BE9FD;font-style:italic">JwtFindProviderError</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#FF79C6">where</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">    Provider</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Clone</span><span style="color:#FF79C6"> +</span><span style="color:#8BE9FD;font-style:italic"> JwtIssuers</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#50FA7B">    decode_jwt_header</span><span style="color:#F8F8F2">(jwt)</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">map_err</span><span style="color:#F8F8F2">(JwtFindProviderError</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">from)</span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> token_data </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> dangerous</span><span style="color:#FF79C6">::</span><span style="color:#50FA7B">insecure_decode</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">UnsafeClaims</span><span style="color:#F8F8F2">>(jwt)</span></span>
<span class="line"><span style="color:#FF79C6">        .</span><span style="color:#50FA7B">map_err</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">|</span><span style="color:#F8F8F2">e</span><span style="color:#FF79C6">|</span><span style="color:#F8F8F2"> JwtFindProviderError</span><span style="color:#FF79C6">::</span><span style="color:#50FA7B">BadSig</span><span style="color:#F8F8F2">(e</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">to_string</span><span style="color:#F8F8F2">()))</span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#FF79C6"> let</span><span style="color:#8BE9FD;font-style:italic"> Some</span><span style="color:#F8F8F2">(iss) </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> token_data</span><span style="color:#FF79C6">.</span><span style="color:#F8F8F2">claims</span><span style="color:#FF79C6">.</span><span style="color:#F8F8F2">iss</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">as_deref</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#FF79C6"> let</span><span style="color:#8BE9FD;font-style:italic"> Some</span><span style="color:#F8F8F2">((prov, cfg)) </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> providers</span></span>
<span class="line"><span style="color:#FF79C6">            .</span><span style="color:#50FA7B">iter</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#FF79C6">            .</span><span style="color:#50FA7B">find</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">|</span><span style="color:#F8F8F2">(provider, _)</span><span style="color:#FF79C6">|</span><span style="color:#F8F8F2"> provider</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">issuers</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">contains</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">&#x26;</span><span style="color:#F8F8F2">iss))</span></span>
<span class="line"><span style="color:#F8F8F2">        {</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#8BE9FD;font-style:italic"> Ok</span><span style="color:#F8F8F2">((prov</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">clone</span><span style="color:#F8F8F2">(), cfg));</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">    Err</span><span style="color:#F8F8F2">(</span><span style="color:#8BE9FD;font-style:italic">JwtFindProviderError</span><span style="color:#FF79C6">::</span><span style="color:#8BE9FD;font-style:italic">NoMatchingProvider</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Once the provider is identified, the public keys are retrieved. The satellite first checks its cache. If the keys are not there, or the specific <code>kid</code> (key ID) referenced in the JWT header is not found, it fetches fresh keys from Observatory (described in the next chapter), Juno's public infrastructure. There is also a rate limiting mechanism to avoid hammering the key endpoint if something goes wrong. (<a href="https://github.com/junobuild/juno/blob/main/src/libs/auth/src/openid/jwkset/jwks.rs" rel="noopener noreferrer external">source</a>)</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">pub</span><span style="color:#FF79C6"> async</span><span style="color:#FF79C6"> fn</span><span style="color:#50FA7B"> get_or_refresh_jwks</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#F8F8F2">    provider</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#8BE9FD;font-style:italic">OpenIdProvider</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    jwt</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#8BE9FD;font-style:italic">str</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    observatory_id</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Principal</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    auth_heap</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> &#x26;impl</span><span style="color:#8BE9FD;font-style:italic"> AuthHeapStrategy</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> Result</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">Jwks</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">GetOrRefreshJwksError</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> unsafe_kid </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> unsafe_find_jwt_kid</span><span style="color:#F8F8F2">(jwt)</span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // Check cache first</span></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> cached_jwks </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> ...</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#FF79C6"> let</span><span style="color:#8BE9FD;font-style:italic"> Some</span><span style="color:#F8F8F2">(cached_jwks) </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> cached_jwks {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#8BE9FD;font-style:italic"> Ok</span><span style="color:#F8F8F2">(cached_jwks);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // Rate limiting check before fetching</span></span>
<span class="line"><span style="color:#FF79C6">    match</span><span style="color:#50FA7B"> refresh_allowed</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">&#x26;</span><span style="color:#F8F8F2">cached_certificate) {</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">        RefreshStatus</span><span style="color:#FF79C6">::</span><span style="color:#8BE9FD;font-style:italic">Denied</span><span style="color:#FF79C6"> =></span><span style="color:#FF79C6"> return</span><span style="color:#8BE9FD;font-style:italic"> Err</span><span style="color:#F8F8F2">(</span><span style="color:#8BE9FD;font-style:italic">GetOrRefreshJwksError</span><span style="color:#FF79C6">::</span><span style="color:#8BE9FD;font-style:italic">KeyNotFoundCooldown</span><span style="color:#F8F8F2">),</span></span>
<span class="line"><span style="color:#F8F8F2">        _ </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> { </span><span style="color:#6272A4">/* proceed */</span><span style="color:#F8F8F2"> }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // Fetch from Observatory</span></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> fetched_certificate </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> fetch_openid_certificate</span><span style="color:#F8F8F2">(provider, observatory_id)</span><span style="color:#FF79C6">.await?</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    cache_certificate</span><span style="color:#F8F8F2">(provider, </span><span style="color:#FF79C6">&#x26;</span><span style="color:#F8F8F2">fetched_certificate, auth_heap)</span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">    Ok</span><span style="color:#F8F8F2">(fetched_certificate</span><span style="color:#FF79C6">.</span><span style="color:#F8F8F2">jwks)</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>With the public keys in hand, the actual JWT verification happens in <code>verify_openid_jwt</code> (<a href="https://github.com/junobuild/juno/blob/main/src/libs/auth/src/openid/jwt/verify.rs" rel="noopener noreferrer external">source</a>). This is where the signature is checked, the issuer validated, the nonce reconstructed and compared, and the timestamps asserted.</p>
<p>A few things worth highlighting before diving into the code.</p>
<ol>
<li><code>exp</code> validation is disabled intentionally. Instead of relying on the token's own expiration field, the satellite enforces a strict 10-minute window based on <code>iat</code> (issued at). This avoids trusting a field that the token issuer controls and that could in theory be set to a far future date.</li>
<li>The nonce is reconstructed on the satellite side from the salt, and compared against the <code>aud</code> claim in the JWT. If they do not match, the request is rejected. This is the man-in-the-middle protection — even if someone intercepted the JWT, they could not reuse it with a different identity.</li>
</ol>
<blockquote>
<p><code>build_nonce</code> is the Rust mirror of the JavaScript <code>buildNonce</code> function shown earlier — same algorithm, same inputs (salt + caller principal), different language. The symmetry is what makes the verification work end to end.</p>
</blockquote>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">pub</span><span style="color:#FF79C6"> fn</span><span style="color:#50FA7B"> build_nonce</span><span style="color:#F8F8F2">(salt</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#8BE9FD;font-style:italic">Salt</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> String</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#FF79C6"> mut</span><span style="color:#F8F8F2"> hasher </span><span style="color:#FF79C6">=</span><span style="color:#8BE9FD;font-style:italic"> Sha256</span><span style="color:#FF79C6">::</span><span style="color:#50FA7B">new</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    hasher</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">update</span><span style="color:#F8F8F2">(salt);</span></span>
<span class="line"><span style="color:#F8F8F2">    hasher</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">update</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">caller</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">as_slice</span><span style="color:#F8F8F2">());</span></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> hash</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#8BE9FD;font-style:italic">u8</span><span style="color:#F8F8F2">; </span><span style="color:#BD93F9">32</span><span style="color:#F8F8F2">] </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> hasher</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">finalize</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">into</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#BD93F9">    URL_SAFE_NO_PAD</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">encode</span><span style="color:#F8F8F2">(hash)</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>With that in mind, here is the full verification function:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">pub</span><span style="color:#FF79C6"> fn</span><span style="color:#50FA7B"> verify_openid_jwt</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">Claims</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">Custom</span><span style="color:#F8F8F2">>(</span></span>
<span class="line"><span style="color:#F8F8F2">    jwt</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#8BE9FD;font-style:italic">str</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    issuers</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#F8F8F2">[</span><span style="color:#FF79C6">&#x26;</span><span style="color:#8BE9FD;font-style:italic">str</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">    jwks</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#F8F8F2">[</span><span style="color:#8BE9FD;font-style:italic">Jwk</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">    salt</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#8BE9FD;font-style:italic">Salt</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    assert_custom</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Custom</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> Result</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">TokenData</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">Claims</span><span style="color:#F8F8F2">>, </span><span style="color:#8BE9FD;font-style:italic">JwtVerifyError</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#FF79C6">where</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">    Claims</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> DeserializeOwned</span><span style="color:#FF79C6"> +</span><span style="color:#8BE9FD;font-style:italic"> JwtClaims</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">    Custom</span><span style="color:#FF79C6">:</span><span style="color:#50FA7B"> FnOnce</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">&#x26;</span><span style="color:#8BE9FD;font-style:italic">Claims</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> Result</span><span style="color:#F8F8F2">&#x3C;(), </span><span style="color:#8BE9FD;font-style:italic">JwtVerifyError</span><span style="color:#F8F8F2">>,</span></span>
<span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> header </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> decode_jwt_header</span><span style="color:#F8F8F2">(jwt)</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">map_err</span><span style="color:#F8F8F2">(JwtVerifyError</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">from)</span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> kid </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> header</span><span style="color:#FF79C6">.</span><span style="color:#F8F8F2">kid</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">ok_or</span><span style="color:#F8F8F2">(JwtVerifyError</span><span style="color:#FF79C6">::</span><span style="color:#8BE9FD;font-style:italic">MissingKid</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> jwk </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> pick_key</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">&#x26;</span><span style="color:#F8F8F2">kid, jwks)</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">ok_or</span><span style="color:#F8F8F2">(JwtVerifyError</span><span style="color:#FF79C6">::</span><span style="color:#8BE9FD;font-style:italic">NoKeyForKid</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> (n, e) </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> match</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">&#x26;</span><span style="color:#F8F8F2">jwk</span><span style="color:#FF79C6">.</span><span style="color:#F8F8F2">kty, </span><span style="color:#FF79C6">&#x26;</span><span style="color:#F8F8F2">jwk</span><span style="color:#FF79C6">.</span><span style="color:#F8F8F2">params) {</span></span>
<span class="line"><span style="color:#F8F8F2">        (</span><span style="color:#8BE9FD;font-style:italic">JwkType</span><span style="color:#FF79C6">::</span><span style="color:#8BE9FD;font-style:italic">Rsa</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">JwkParams</span><span style="color:#FF79C6">::</span><span style="color:#50FA7B">Rsa</span><span style="color:#F8F8F2">(params)) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">&#x26;</span><span style="color:#F8F8F2">params</span><span style="color:#FF79C6">.</span><span style="color:#F8F8F2">n, </span><span style="color:#FF79C6">&#x26;</span><span style="color:#F8F8F2">params</span><span style="color:#FF79C6">.</span><span style="color:#F8F8F2">e),</span></span>
<span class="line"><span style="color:#F8F8F2">        _ </span><span style="color:#FF79C6">=></span><span style="color:#FF79C6"> return</span><span style="color:#8BE9FD;font-style:italic"> Err</span><span style="color:#F8F8F2">(</span><span style="color:#8BE9FD;font-style:italic">JwtVerifyError</span><span style="color:#FF79C6">::</span><span style="color:#8BE9FD;font-style:italic">WrongKeyType</span><span style="color:#F8F8F2">),</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> key </span><span style="color:#FF79C6">=</span><span style="color:#8BE9FD;font-style:italic"> DecodingKey</span><span style="color:#FF79C6">::</span><span style="color:#50FA7B">from_rsa_components</span><span style="color:#F8F8F2">(n, e)</span></span>
<span class="line"><span style="color:#FF79C6">        .</span><span style="color:#50FA7B">map_err</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">|</span><span style="color:#F8F8F2">e</span><span style="color:#FF79C6">|</span><span style="color:#F8F8F2"> JwtVerifyError</span><span style="color:#FF79C6">::</span><span style="color:#50FA7B">BadSig</span><span style="color:#F8F8F2">(e</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">to_string</span><span style="color:#F8F8F2">()))</span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#FF79C6"> mut</span><span style="color:#F8F8F2"> val </span><span style="color:#FF79C6">=</span><span style="color:#8BE9FD;font-style:italic"> Validation</span><span style="color:#FF79C6">::</span><span style="color:#50FA7B">new</span><span style="color:#F8F8F2">(Algorithm</span><span style="color:#FF79C6">::</span><span style="color:#BD93F9">RS256</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    val</span><span style="color:#FF79C6">.</span><span style="color:#F8F8F2">validate_exp </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    val</span><span style="color:#FF79C6">.</span><span style="color:#F8F8F2">validate_nbf </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    val</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">set_issuer</span><span style="color:#F8F8F2">(issuers);</span></span>
<span class="line"><span style="color:#F8F8F2">    val</span><span style="color:#FF79C6">.</span><span style="color:#F8F8F2">validate_aud </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> token </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#50FA7B">        decode</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">Claims</span><span style="color:#F8F8F2">>(jwt, </span><span style="color:#FF79C6">&#x26;</span><span style="color:#F8F8F2">key, </span><span style="color:#FF79C6">&#x26;</span><span style="color:#F8F8F2">val)</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">map_err</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">|</span><span style="color:#F8F8F2">e</span><span style="color:#FF79C6">|</span><span style="color:#F8F8F2"> JwtVerifyError</span><span style="color:#FF79C6">::</span><span style="color:#50FA7B">BadSig</span><span style="color:#F8F8F2">(e</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">to_string</span><span style="color:#F8F8F2">()))</span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> c </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#F8F8F2">token</span><span style="color:#FF79C6">.</span><span style="color:#F8F8F2">claims;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> nonce </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> build_nonce</span><span style="color:#F8F8F2">(salt);</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> c</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">nonce</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">!=</span><span style="color:#8BE9FD;font-style:italic"> Some</span><span style="color:#F8F8F2">(nonce</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">as_str</span><span style="color:#F8F8F2">()) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#8BE9FD;font-style:italic"> Err</span><span style="color:#F8F8F2">(</span><span style="color:#8BE9FD;font-style:italic">JwtVerifyError</span><span style="color:#FF79C6">::</span><span style="color:#50FA7B">BadClaim</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">"nonce"</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">to_string</span><span style="color:#F8F8F2">()));</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    assert_custom</span><span style="color:#F8F8F2">(c)</span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> now_ns </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> now_ns</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#BD93F9"> MAX_VALIDITY_WINDOW_NS</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> u64</span><span style="color:#FF79C6"> =</span><span style="color:#BD93F9"> 10</span><span style="color:#FF79C6"> *</span><span style="color:#BD93F9"> 60</span><span style="color:#FF79C6"> *</span><span style="color:#BD93F9"> 1_000_000_000</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#BD93F9"> IAT_FUTURE_SKEW_NS</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> u64</span><span style="color:#FF79C6"> =</span><span style="color:#BD93F9"> 2</span><span style="color:#FF79C6"> *</span><span style="color:#BD93F9"> 60</span><span style="color:#FF79C6"> *</span><span style="color:#BD93F9"> 1_000_000_000</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> iat_ns </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> c</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">iat</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">ok_or</span><span style="color:#F8F8F2">(JwtVerifyError</span><span style="color:#FF79C6">::</span><span style="color:#50FA7B">BadClaim</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">"iat"</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">to_string</span><span style="color:#F8F8F2">()))</span><span style="color:#FF79C6">?</span></span>
<span class="line"><span style="color:#FF79C6">        .</span><span style="color:#50FA7B">saturating_mul</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">1_000_000_000</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> now_ns </span><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2"> iat_ns</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">saturating_sub</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">IAT_FUTURE_SKEW_NS</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#8BE9FD;font-style:italic"> Err</span><span style="color:#F8F8F2">(</span><span style="color:#8BE9FD;font-style:italic">JwtVerifyError</span><span style="color:#FF79C6">::</span><span style="color:#50FA7B">BadClaim</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">"iat_future"</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">to_string</span><span style="color:#F8F8F2">()));</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> now_ns </span><span style="color:#FF79C6">></span><span style="color:#F8F8F2"> iat_ns</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">saturating_add</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">MAX_VALIDITY_WINDOW_NS</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#8BE9FD;font-style:italic"> Err</span><span style="color:#F8F8F2">(</span><span style="color:#8BE9FD;font-style:italic">JwtVerifyError</span><span style="color:#FF79C6">::</span><span style="color:#50FA7B">BadClaim</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">"iat_expired"</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">to_string</span><span style="color:#F8F8F2">()));</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">    Ok</span><span style="color:#F8F8F2">(token)</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>The custom claim assertion is passed as a callback — <code>assert_custom</code> — keeping the core verification logic generic and reusable as I reuse the same validation for other flows such as user browser-based authentications.</p>
<p>For the GitHub Actions flow, this callback checks that the <code>repository</code> claim matches one of the configured repositories, and optionally that the <code>ref</code> (branch or PR) is in the allowed list. In other words, it checks the provided information against what the developer has configured.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">let</span><span style="color:#F8F8F2"> assert_repository </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> |</span><span style="color:#F8F8F2">claims</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#8BE9FD;font-style:italic">AutomationClaims</span><span style="color:#FF79C6">|</span><span style="color:#FF79C6"> -></span><span style="color:#8BE9FD;font-style:italic"> Result</span><span style="color:#F8F8F2">&#x3C;(), </span><span style="color:#8BE9FD;font-style:italic">JwtVerifyError</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> repo_key </span><span style="color:#FF79C6">=</span><span style="color:#8BE9FD;font-style:italic"> RepositoryKey</span><span style="color:#F8F8F2"> { owner, name };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> repo_config </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> config</span><span style="color:#FF79C6">.</span><span style="color:#F8F8F2">repositories</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">&#x26;</span><span style="color:#F8F8F2">repo_key)</span></span>
<span class="line"><span style="color:#FF79C6">        .</span><span style="color:#50FA7B">ok_or_else</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">||</span><span style="color:#F8F8F2"> JwtVerifyError</span><span style="color:#FF79C6">::</span><span style="color:#50FA7B">BadClaim</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">"repository_unauthorized"</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">to_string</span><span style="color:#F8F8F2">()))</span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#FF79C6"> let</span><span style="color:#8BE9FD;font-style:italic"> Some</span><span style="color:#F8F8F2">(allowed_refs) </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#F8F8F2">repo_config</span><span style="color:#FF79C6">.</span><span style="color:#F8F8F2">refs {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#FF79C6"> !</span><span style="color:#F8F8F2">allowed_refs</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">contains</span><span style="color:#F8F8F2">(ref_claim) {</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#8BE9FD;font-style:italic"> Err</span><span style="color:#F8F8F2">(</span><span style="color:#8BE9FD;font-style:italic">JwtVerifyError</span><span style="color:#FF79C6">::</span><span style="color:#50FA7B">BadClaim</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">"ref_unauthorized"</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">to_string</span><span style="color:#F8F8F2">()));</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">    Ok</span><span style="color:#F8F8F2">(())</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>If all verifications pass, the identity is saved as an allowed access key on the satellite, and the workflow can proceed.</p>
<p>In practice, the full authentication flow looks like this (<a href="https://github.com/junobuild/juno/blob/main/src/libs/satellite/src/automation/authenticate.rs" rel="noopener noreferrer external">source</a>):</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">pub</span><span style="color:#FF79C6"> async</span><span style="color:#FF79C6"> fn</span><span style="color:#50FA7B"> openid_authenticate_automation</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#F8F8F2">    args</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#8BE9FD;font-style:italic">OpenIdPrepareAutomationArgs</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> Result</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">AuthenticateAutomationResult</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">String</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> providers </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> get_automation_providers</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">&#x26;</span><span style="color:#8BE9FD;font-style:italic">AuthHeap</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> prepared_automation </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> prepare</span><span style="color:#FF79C6">::</span><span style="color:#50FA7B">openid_prepare_automation</span><span style="color:#F8F8F2">(args, </span><span style="color:#FF79C6">&#x26;</span><span style="color:#F8F8F2">providers)</span><span style="color:#FF79C6">.await</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> result </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> match</span><span style="color:#F8F8F2"> prepared_automation {</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">        Ok</span><span style="color:#F8F8F2">((automation, provider, credential)) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">            if</span><span style="color:#FF79C6"> let</span><span style="color:#8BE9FD;font-style:italic"> Err</span><span style="color:#F8F8F2">(err) </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> save_unique_token_jti</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">&#x26;</span><span style="color:#F8F8F2">automation, </span><span style="color:#FF79C6">&#x26;</span><span style="color:#F8F8F2">provider, </span><span style="color:#FF79C6">&#x26;</span><span style="color:#F8F8F2">credential) {</span></span>
<span class="line"><span style="color:#FF79C6">                return</span><span style="color:#8BE9FD;font-style:italic"> Ok</span><span style="color:#F8F8F2">(</span><span style="color:#8BE9FD;font-style:italic">Err</span><span style="color:#F8F8F2">(</span><span style="color:#8BE9FD;font-style:italic">AuthenticationAutomationError</span><span style="color:#FF79C6">::</span><span style="color:#50FA7B">SaveUniqueJtiToken</span><span style="color:#F8F8F2">(err)));</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">            if</span><span style="color:#FF79C6"> let</span><span style="color:#8BE9FD;font-style:italic"> Err</span><span style="color:#F8F8F2">(err) </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> save_workflow_metadata</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">&#x26;</span><span style="color:#F8F8F2">provider, </span><span style="color:#FF79C6">&#x26;</span><span style="color:#F8F8F2">credential) {</span></span>
<span class="line"><span style="color:#FF79C6">                return</span><span style="color:#8BE9FD;font-style:italic"> Ok</span><span style="color:#F8F8F2">(</span><span style="color:#8BE9FD;font-style:italic">Err</span><span style="color:#F8F8F2">(</span><span style="color:#8BE9FD;font-style:italic">AuthenticationAutomationError</span><span style="color:#FF79C6">::</span><span style="color:#50FA7B">SaveWorkflowMetadata</span><span style="color:#F8F8F2">(err)));</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">            if</span><span style="color:#FF79C6"> let</span><span style="color:#8BE9FD;font-style:italic"> Err</span><span style="color:#F8F8F2">(err) </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> register_controller</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">&#x26;</span><span style="color:#F8F8F2">automation, </span><span style="color:#FF79C6">&#x26;</span><span style="color:#F8F8F2">provider, </span><span style="color:#FF79C6">&#x26;</span><span style="color:#F8F8F2">credential) {</span></span>
<span class="line"><span style="color:#FF79C6">                return</span><span style="color:#8BE9FD;font-style:italic"> Ok</span><span style="color:#F8F8F2">(</span><span style="color:#8BE9FD;font-style:italic">Err</span><span style="color:#F8F8F2">(</span><span style="color:#8BE9FD;font-style:italic">AuthenticationAutomationError</span><span style="color:#FF79C6">::</span><span style="color:#50FA7B">RegisterController</span><span style="color:#F8F8F2">(err)));</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">            Ok</span><span style="color:#F8F8F2">(automation)</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">        Err</span><span style="color:#F8F8F2">(err) </span><span style="color:#FF79C6">=></span><span style="color:#8BE9FD;font-style:italic"> Err</span><span style="color:#F8F8F2">(</span><span style="color:#8BE9FD;font-style:italic">AuthenticationAutomationError</span><span style="color:#FF79C6">::</span><span style="color:#50FA7B">PrepareAutomation</span><span style="color:#F8F8F2">(err)),</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">    Ok</span><span style="color:#F8F8F2">(result)</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>One last thing worth mentioning: JTI replay protection. The verification function intentionally does not handle replay attacks on its own — that responsibility is left to the consumer of the crate (here again, I love abstraction therefore I use multiple layers). That's why <code>save_unique_token_jti</code> records the JTI only if it hasn't been seen before, rejecting any future request using the same token.</p>
<p>Then the workflow metadata is saved — this is what powers the deployment history displayed in the Juno console — and finally the identity is registered as a controller, the ephemeral access key that the CLI will use for the rest of the workflow.</p>
<hr />
<h2>The Observatory</h2>
<p>The Observatory is Juno's public infrastructure. Among other things, it is responsible for keeping the OIDC public keys fresh so that satellites do not have to fetch them on every request.</p>
<p>The reason it exists as a separate piece of infrastructure is practical: OIDC public keys are the same for everyone. There is no point in having each satellite — and by extension each developer — independently fetch and pay for the same HTTP outcall. Having a single place fetch and cache the keys reduces redundant network traffic on the subnet and keeps costs down for everyone.</p>
<p>The core of it is a recurring timer that fetches the JWKS from the provider's URL and stores it. If the fetch fails — which can happen during key rotation, when HTTPS outcall responses are not yet consistent across nodes — it retries with exponential backoff. (<a href="https://github.com/junobuild/juno/blob/main/src/observatory/src/openid/certificate.rs" rel="noopener noreferrer external">source</a>)</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">pub</span><span style="color:#FF79C6"> fn</span><span style="color:#50FA7B"> schedule_certificate_update</span><span style="color:#F8F8F2">(provider</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> OpenIdProvider</span><span style="color:#F8F8F2">, delay</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Option</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">u64</span><span style="color:#F8F8F2">>) {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#50FA7B"> assert_scheduler_running</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">&#x26;</span><span style="color:#F8F8F2">provider)</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">is_err</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    set_timer</span><span style="color:#F8F8F2">(Duration</span><span style="color:#FF79C6">::</span><span style="color:#50FA7B">from_secs</span><span style="color:#F8F8F2">(delay</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">unwrap_or</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">)), </span><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> move</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        let</span><span style="color:#F8F8F2"> result </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> fetch_and_save_certificate</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">&#x26;</span><span style="color:#F8F8F2">provider)</span><span style="color:#FF79C6">.await</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        let</span><span style="color:#F8F8F2"> next_delay </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> if</span><span style="color:#F8F8F2"> result</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">is_ok</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#BD93F9">            FETCH_CERTIFICATE_INTERVAL</span></span>
<span class="line"><span style="color:#F8F8F2">        } </span><span style="color:#FF79C6">else</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">            let</span><span style="color:#F8F8F2"> backoff </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> match</span><span style="color:#F8F8F2"> delay {</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">                Some</span><span style="color:#F8F8F2">(delay) </span><span style="color:#FF79C6">if</span><span style="color:#F8F8F2"> delay </span><span style="color:#FF79C6">&#x3C;</span><span style="color:#BD93F9"> FETCH_CERTIFICATE_INTERVAL</span><span style="color:#FF79C6"> =></span><span style="color:#F8F8F2"> delay</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">saturating_mul</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">2</span><span style="color:#F8F8F2">),</span></span>
<span class="line"><span style="color:#F8F8F2">                _ </span><span style="color:#FF79C6">=></span><span style="color:#BD93F9"> 60</span><span style="color:#FF79C6"> *</span><span style="color:#BD93F9"> 2</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">            };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">            min</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">FETCH_CERTIFICATE_INTERVAL</span><span style="color:#F8F8F2">, backoff)</span></span>
<span class="line"><span style="color:#F8F8F2">        };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">        schedule_certificate_update</span><span style="color:#F8F8F2">(provider, </span><span style="color:#8BE9FD;font-style:italic">Some</span><span style="color:#F8F8F2">(next_delay));</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>The function is recursive — each run schedules the next one. On success, it waits for the standard interval. On failure, it starts at a 2-minute delay and doubles on each subsequent failure, capping at the standard interval. This matters in practice: during a key rotation, GitHub may temporarily serve inconsistent responses across requests, so retrying too aggressively would just produce more errors.</p>
<p>The actual fetch is straightforward:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> fn</span><span style="color:#50FA7B"> fetch_and_save_certificate</span><span style="color:#F8F8F2">(provider</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#8BE9FD;font-style:italic">OpenIdProvider</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> Result</span><span style="color:#F8F8F2">&#x3C;(), </span><span style="color:#8BE9FD;font-style:italic">String</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> http_result </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> get_certificate</span><span style="color:#F8F8F2">(provider)</span><span style="color:#FF79C6">.await?</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> jwks </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> from_slice</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">Jwks</span><span style="color:#F8F8F2">>(</span><span style="color:#FF79C6">&#x26;</span><span style="color:#F8F8F2">http_result</span><span style="color:#FF79C6">.</span><span style="color:#F8F8F2">body)</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">map_err</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">|</span><span style="color:#F8F8F2">e</span><span style="color:#FF79C6">|</span><span style="color:#F8F8F2"> e</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">to_string</span><span style="color:#F8F8F2">())</span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    set_openid_certificate</span><span style="color:#F8F8F2">(provider, </span><span style="color:#FF79C6">&#x26;</span><span style="color:#F8F8F2">jwks);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">    Ok</span><span style="color:#F8F8F2">(())</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>It fetches, parses the JWKS from JSON, and stores it. The URL comes from the provider implementation — each provider knows its own JWKS endpoint. (<a href="https://github.com/junobuild/juno/blob/main/src/libs/auth/src/openid/impls.rs" rel="noopener noreferrer external">source</a>)</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">impl</span><span style="color:#8BE9FD;font-style:italic"> OpenIdProvider</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    pub</span><span style="color:#FF79C6"> fn</span><span style="color:#50FA7B"> jwks_url</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">&#x26;</span><span style="color:#BD93F9;font-style:italic">self</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">-></span><span style="color:#FF79C6"> &#x26;</span><span style="color:#F8F8F2">'</span><span style="color:#8BE9FD;font-style:italic">static</span><span style="color:#8BE9FD;font-style:italic"> str</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        match</span><span style="color:#BD93F9;font-style:italic"> self</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">            Self</span><span style="color:#FF79C6">::</span><span style="color:#8BE9FD;font-style:italic">Google</span><span style="color:#FF79C6"> =></span><span style="color:#F1FA8C"> "https://www.googleapis.com/oauth2/v3/certs"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">            Self</span><span style="color:#FF79C6">::</span><span style="color:#8BE9FD;font-style:italic">GitHubAuth</span><span style="color:#FF79C6"> =></span><span style="color:#F1FA8C"> "https://api.juno.build/v1/auth/certs"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">            Self</span><span style="color:#FF79C6">::</span><span style="color:#8BE9FD;font-style:italic">GitHubActions</span><span style="color:#FF79C6"> =></span><span style="color:#F1FA8C"> "https://token.actions.githubusercontent.com/.well-known/jwks"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>The HTTP outcall itself uses the Internet Computer's <code>http_request_outcall</code>, which allows programs to make outbound HTTPS requests. The <code>is_replicated</code> flag ensures the call goes through consensus — all nodes in the subnet must agree on the response before it is accepted. (<a href="https://github.com/junobuild/juno/blob/main/src/observatory/src/openid/http/request.rs" rel="noopener noreferrer external">source</a>)</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">fn</span><span style="color:#50FA7B"> get_request</span><span style="color:#F8F8F2">(provider</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#8BE9FD;font-style:italic">OpenIdProvider</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> HttpRequestArgs</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> url </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> provider</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">jwks_url</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> request_headers </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> vec!</span><span style="color:#F8F8F2">[</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">        HttpHeader</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">            name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> "Accept"</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">into</span><span style="color:#F8F8F2">(),</span></span>
<span class="line"><span style="color:#F8F8F2">            value</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> "application/json"</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">into</span><span style="color:#F8F8F2">(),</span></span>
<span class="line"><span style="color:#F8F8F2">        },</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">        HttpHeader</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">            name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> "User-Agent"</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">into</span><span style="color:#F8F8F2">(),</span></span>
<span class="line"><span style="color:#F8F8F2">            value</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> "juno_observatory"</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">into</span><span style="color:#F8F8F2">(),</span></span>
<span class="line"><span style="color:#F8F8F2">        },</span></span>
<span class="line"><span style="color:#F8F8F2">    ];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">    HttpRequestArgs</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        url</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> url</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">to_string</span><span style="color:#F8F8F2">(),</span></span>
<span class="line"><span style="color:#F8F8F2">        method</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HttpMethod</span><span style="color:#FF79C6">::</span><span style="color:#BD93F9">GET</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        body</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> None</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        max_response_bytes</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Some</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">FETCH_MAX_RESPONSE_BYTES</span><span style="color:#F8F8F2">),</span></span>
<span class="line"><span style="color:#F8F8F2">        transform</span><span style="color:#FF79C6">:</span><span style="color:#50FA7B"> param_transform</span><span style="color:#F8F8F2">(),</span></span>
<span class="line"><span style="color:#F8F8F2">        headers</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> request_headers,</span></span>
<span class="line"><span style="color:#F8F8F2">        is_replicated</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Some</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">true</span><span style="color:#F8F8F2">),</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Once stored, the JWKS is available to any satellite that needs to verify an OIDC token — without having to make an outbound call themselves.</p>
<hr />
<h2>References</h2>
<ul>
<li><a href="https://docs.github.com/en/actions/concepts/security/openid-connect" rel="noopener noreferrer external">GitHub Actions — OpenID Connect</a></li>
<li><a href="https://github.com/junobuild/juno-action" rel="noopener noreferrer external">junobuild/juno-action</a></li>
<li><a href="https://github.com/junobuild/juno-js" rel="noopener noreferrer external">junobuild/juno-js</a> — includes <code>@junobuild/auth</code></li>
<li><a href="https://github.com/junobuild/juno" rel="noopener noreferrer external">junobuild/juno</a> — Satellite, Observatory, and all Rust crates</li>
<li><a href="https://docs.internetcomputer.org/building-apps/network-features/using-http/https-outcalls/overview" rel="noopener noreferrer external">Internet Computer — HTTPS Outcalls</a></li>
</ul>
<hr />
<h2>Conclusion</h2>
<p>That's about it. If you are still here, congratulations! I'm well aware this blog post wasn't easy to follow and most probably dropped while going through all the steps — not speaking of my writing. I hope anyway it was interesting enough to give you some ideas, and if you have any suggestions for improvements, please let me know!</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Better Type Safety in Svelte Guard Components]]></title>
          <description><![CDATA[Leveraging snippet parameters to make async data guard components properly type-safe.]]></description>
          <link>https://daviddalbusco.com/blog/better-type-safety-in-svelte-guard-components/</link>
          <pubDate>Sat, 14 Feb 2026 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/maxim-berg-ANuuRuCRRAc-unsplash.jpg" alt="" / loading="lazy"></p>
<blockquote>
<p>Photo by <a href="https://medium.com/r/?url=https%3A%2F%2Funsplash.com%2Ffr%2F%40maxberg%3Futm_source%3Dunsplash%26utm_medium%3Dreferral%26utm_content%3DcreditCopyText" rel="noopener noreferrer external">Maxim Berg</a> on <a href="https://unsplash.com/fr/photos/un-fond-bleu-et-violet-avec-des-lignes-verticales-ANuuRuCRRAc?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></p>
</blockquote>
<hr />
<p>I recently discovered a better pattern for guard components in Svelte that solves a minor TypeScript type narrowing issue I'd been working around for some time.</p>
<hr />
<h2>The Problem</h2>
<p>In my Svelte apps, I often need to handle data that loads asynchronously on the client side. I use a state pattern: <code>undefined</code> means still loading, <code>null</code> means nothing found, and an actual value means data is loaded.</p>
<p>To reflect those states visually, for example by displaying a good old spinner until data are fetched followed by fading on to the content or a message informing nothing was found, I use a guard component.</p>
<p>If e.g. let's say I'm loading an object called <code>Article</code>, I would be doing something as follows:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2">script lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ts</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">></span></span>
<span class="line"><span style="color:#FF79C6">    import</span><span style="color:#FF79C6"> type</span><span style="color:#F8F8F2"> { Snippet } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">svelte</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">    import</span><span style="color:#F8F8F2"> Spinner </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">$lib/components/ui/Spinner.svelte</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">    import</span><span style="color:#FF79C6"> type</span><span style="color:#F8F8F2"> {Article} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">$lib/types/article</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    interface</span><span style="color:#8BE9FD;font-style:italic"> Props</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        article</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Article</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        children</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Snippet</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> { article, children }</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Props</span><span style="color:#FF79C6"> =</span><span style="color:#50FA7B"> $props</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">&#x3C;/</span><span style="color:#F8F8F2">script</span><span style="color:#FF79C6">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">{#</span><span style="color:#FF79C6">if</span><span style="color:#F8F8F2"> article </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">}</span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Spinner</span><span style="color:#F8F8F2">>Loading</span><span style="color:#FF79C6">...&#x3C;/</span><span style="color:#F8F8F2">Spinner</span><span style="color:#FF79C6">></span></span>
<span class="line"><span style="color:#F8F8F2">{:</span><span style="color:#FF79C6">else</span><span style="color:#FF79C6"> if</span><span style="color:#F8F8F2"> article </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> null</span><span style="color:#F8F8F2">}</span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">p</span><span style="color:#F8F8F2">>Not found.</span><span style="color:#FF79C6">&#x3C;/</span><span style="color:#F8F8F2">p</span><span style="color:#FF79C6">></span></span>
<span class="line"><span style="color:#F8F8F2">{:</span><span style="color:#FF79C6">else</span><span style="color:#F8F8F2">}</span></span>
<span class="line"><span style="color:#F8F8F2">    {@</span><span style="color:#50FA7B;font-style:italic">render</span><span style="color:#50FA7B"> children</span><span style="color:#F8F8F2">()}</span></span>
<span class="line"><span style="color:#F8F8F2">{</span><span style="color:#FF79C6">/if</span><span style="color:#F8F8F2">}</span></span></code></pre><p>This worked fine, but there was a TypeScript quirk: when I used this component, TypeScript couldn't infer that <code>article</code> was guaranteed to be defined when <code>children</code> rendered. So I'd still need type assertions in the consuming code.</p>
<p>For example:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2">script lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ts</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">></span></span>
<span class="line"><span style="color:#FF79C6">    import</span><span style="color:#F8F8F2"> { onMount } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">svelte</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">    import</span><span style="color:#FF79C6"> type</span><span style="color:#F8F8F2"> { Article } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">$lib/types/article</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">    import</span><span style="color:#F8F8F2"> Guard </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">$lib/Guard.svelte</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">    import</span><span style="color:#F8F8F2"> { load } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">$lib/services/article.services</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> article </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> $state</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Article</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#F8F8F2">>();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#50FA7B"> init</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        article </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> load</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    onMount</span><span style="color:#F8F8F2">(init);</span></span>
<span class="line"><span style="color:#FF79C6">&#x3C;/</span><span style="color:#F8F8F2">script</span><span style="color:#FF79C6">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2">Guard {article}</span><span style="color:#FF79C6">></span></span>
<span class="line"><span style="color:#F8F8F2">    {#</span><span style="color:#FF79C6">if</span><span style="color:#F8F8F2"> article </span><span style="color:#FF79C6">!==</span><span style="color:#BD93F9"> undefined</span><span style="color:#FF79C6"> &#x26;&#x26;</span><span style="color:#F8F8F2"> article </span><span style="color:#FF79C6">!==</span><span style="color:#BD93F9"> null</span><span style="color:#F8F8F2">}</span></span>
<span class="line"><span style="color:#F8F8F2">        Loaded: {article.title}</span></span>
<span class="line"><span style="color:#F8F8F2">    {</span><span style="color:#FF79C6">/if</span><span style="color:#F8F8F2">}</span></span>
<span class="line"><span style="color:#FF79C6">&#x3C;/</span><span style="color:#F8F8F2">Guard</span><span style="color:#FF79C6">></span></span></code></pre><p>Those assertions (<code>article !== undefined &amp;&amp; article !== null</code>) defeat a bit the whole purpose of having strong types.</p>
<hr />
<h2>The Solution</h2>
<p>The solution turned out to be surprisingly simple: use a custom snippet (not children) and pass the loaded value as a parameter.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2">script lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ts</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">></span></span>
<span class="line"><span style="color:#FF79C6">    import</span><span style="color:#FF79C6"> type</span><span style="color:#F8F8F2"> { Snippet } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">svelte</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">    import</span><span style="color:#F8F8F2"> Spinner </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">$lib/components/ui/Spinner.svelte</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">    import</span><span style="color:#FF79C6"> type</span><span style="color:#F8F8F2"> {Article} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">$lib/types/article</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    interface</span><span style="color:#8BE9FD;font-style:italic"> Props</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        article</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Article</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        content</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Snippet</span><span style="color:#F8F8F2">&#x3C;[</span><span style="color:#FFB86C;font-style:italic">Article</span><span style="color:#F8F8F2">]>;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> { article, content }</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Props</span><span style="color:#FF79C6"> =</span><span style="color:#50FA7B"> $props</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">&#x3C;/</span><span style="color:#F8F8F2">script</span><span style="color:#FF79C6">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">{#</span><span style="color:#FF79C6">if</span><span style="color:#F8F8F2"> article </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">}</span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Spinner</span><span style="color:#F8F8F2">>Loading</span><span style="color:#FF79C6">...&#x3C;/</span><span style="color:#F8F8F2">Spinner</span><span style="color:#FF79C6">></span></span>
<span class="line"><span style="color:#F8F8F2">{:</span><span style="color:#FF79C6">else</span><span style="color:#FF79C6"> if</span><span style="color:#F8F8F2"> article </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> null</span><span style="color:#F8F8F2">}</span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">p</span><span style="color:#F8F8F2">>Not found.</span><span style="color:#FF79C6">&#x3C;/</span><span style="color:#F8F8F2">p</span><span style="color:#FF79C6">></span></span>
<span class="line"><span style="color:#F8F8F2">{:</span><span style="color:#FF79C6">else</span><span style="color:#F8F8F2">}</span></span>
<span class="line"><span style="color:#F8F8F2">    {@</span><span style="color:#50FA7B;font-style:italic">render</span><span style="color:#50FA7B"> content</span><span style="color:#F8F8F2">(article)}</span></span>
<span class="line"><span style="color:#F8F8F2">{</span><span style="color:#FF79C6">/if</span><span style="color:#F8F8F2">}</span></span></code></pre><p>Which gets implemented by the consumer like:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2">Guard {article}</span><span style="color:#FF79C6">></span></span>
<span class="line"><span style="color:#F8F8F2">    {#snippet </span><span style="color:#50FA7B">content</span><span style="color:#F8F8F2">(article)}</span></span>
<span class="line"><span style="color:#F8F8F2">        Loaded: {article.title}</span></span>
<span class="line"><span style="color:#F8F8F2">    {</span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2">snippet}</span></span>
<span class="line"><span style="color:#FF79C6">&#x3C;/</span><span style="color:#F8F8F2">Guard</span><span style="color:#FF79C6">></span></span></code></pre><p>Now when I use this guard component, I get full type safety.</p>
<hr />
<h2>Why This Works</h2>
<p>By changing from <code>Snippet</code> to <code>Snippet&lt;[Article]&gt;</code> and passing the validated <code>article</code> as a parameter, TypeScript finally understands the flow:</p>
<ol>
<li>The guard component checks that <code>article</code> is neither <code>undefined</code> nor <code>null</code></li>
<li>Only then does it render the snippet, passing <code>article</code> as a function parameter</li>
<li>The consuming code receives a properly typed <code>Article</code> object - not <code>Article | undefined | null</code></li>
</ol>
<p>Because the snippet receives <code>article</code> as a parameter only after it passes through the validation checks, TypeScript knows it's safe.</p>
<hr />
<h2>Conclusion</h2>
<p>This is one of those small comfort patterns that I really enjoy. It makes my code base cleaner and type safer.</p>
<p>Happy coding ☀️<br />
David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[How to Type Modularized Handlers in Elysia]]></title>
          <description><![CDATA[A practical journey through typing Elysia handlers once you extract them from inline routes.]]></description>
          <link>https://daviddalbusco.com/blog/how-to-type-modularized-handlers-in-elysia/</link>
          <pubDate>Fri, 16 Jan 2026 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/steve-johnson-dl6IqQFFnjM-unsplash.jpg" alt="" / loading="lazy"></p>
<blockquote>
<p>Photo by <a href="https://unsplash.com/fr/@steve_j?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Steve Johnson</a> on <a href="https://unsplash.com/fr/photos/image-generee-par-ordinateur-dun-groupe-de-cubes-dl6IqQFFnjM?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></p>
</blockquote>
<hr />
<p>I just started using <a href="https://bun.com/" rel="noopener noreferrer external">Bun</a> and <a href="https://elysiajs.com/" rel="noopener noreferrer external">Elysia</a> and really enjoying it so far, but it took me a chunk of today to figure out how to properly type handlers once I started modularizing my code.</p>
<p>If you're hitting the same issue, here's my solution for both standard validation and JWT-authenticated routes.</p>
<hr />
<h2>Validation and Context</h2>
<p>If I understand correctly, Elysia's end-to-end type safety comes from the fact that all types are inferred when you define <a href="https://elysiajs.com/essential/validation.html#schema-type" rel="noopener noreferrer external">validation</a>. This is absolutely amazing if you follow the ergonomics of chaining all routes.</p>
<p>For example, in the following snippet, the content (<code>hello</code>) of the body is validated and inferred. Fantastic.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Elysia, t } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">elysia</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> app </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Elysia</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#E9F284">        "</span><span style="color:#F1FA8C">/</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        ({ </span><span style="color:#FFB86C;font-style:italic">body</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">            const</span><span style="color:#F8F8F2"> { hello } </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> body; </span><span style="color:#6272A4">// &#x3C;- Magical type safety</span></span>
<span class="line"><span style="color:#F8F8F2">        },</span></span>
<span class="line"><span style="color:#F8F8F2">        {</span></span>
<span class="line"><span style="color:#F8F8F2">            body</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> t.</span><span style="color:#50FA7B">Object</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">                hello</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> t.</span><span style="color:#50FA7B">String</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">            })</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    )</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">listen</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">3000</span><span style="color:#F8F8F2">);</span></span></code></pre><p>However, if I refactor the snippet to extract its <a href="https://elysiajs.com/essential/handler.html" rel="noopener noreferrer external">handler</a>, even in the same module, I lose the types and the body becomes <code>any</code>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Elysia, t } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">elysia</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// TS7031: Binding element body implicitly has an any type.</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> handler</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">body</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { hello } </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> body;</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> app </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Elysia</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, handler, {</span></span>
<span class="line"><span style="color:#F8F8F2">        body</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> t.</span><span style="color:#50FA7B">Object</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">            hello</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> t.</span><span style="color:#50FA7B">String</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">        })</span></span>
<span class="line"><span style="color:#F8F8F2">    })</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">listen</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">3000</span><span style="color:#F8F8F2">);</span></span></code></pre><p>One interesting way to solve this issue — provided in this <a href="https://github.com/elysiajs/elysia/issues/95#issuecomment-1715722631" rel="noopener noreferrer external">answer</a> on GitHub — would be to use <code>typeof</code> the app on the particular route to declare its type. However, this only works if the handler stays in the same module and refactoring it out would introduce recursive imports.</p>
<p>After digging through issues, I discovered that a <code>Context</code> interface can be used to type the handler parameters. However, this doesn't infer the validator types.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { </span><span style="color:#FF79C6">type</span><span style="color:#F8F8F2"> Context, Elysia, t } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">elysia</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> handler</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">body</span><span style="color:#F8F8F2"> }</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Context</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#6272A4">    // TS2339: Property hello does not exist on type unknown</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { hello } </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> body;</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Fortunately, <code>Context</code> is generic. I just needed to extract the schema type.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { </span><span style="color:#FF79C6">type</span><span style="color:#F8F8F2"> Context, Elysia, t } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">elysia</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> HandlerSchema </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> t.</span><span style="color:#50FA7B">Object</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    hello</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> t.</span><span style="color:#50FA7B">String</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// TS2749: HandlerSchema refers to a value, but is being used as a type here. Did you mean typeof HandlerSchema?</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> handler</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">body</span><span style="color:#F8F8F2"> }</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Context</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">HandlerSchema</span><span style="color:#F8F8F2">>) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { hello } </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> body;</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> app </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Elysia</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, handler, {</span></span>
<span class="line"><span style="color:#F8F8F2">        body</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> HandlerSchema</span></span>
<span class="line"><span style="color:#F8F8F2">    })</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">listen</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">3000</span><span style="color:#F8F8F2">);</span></span></code></pre><p>The next question was how to infer the type of the schema, as using <code>typeof</code> as suggested by my editor didn't work.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { </span><span style="color:#FF79C6">type</span><span style="color:#F8F8F2"> Context, Elysia, t } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">elysia</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> HandlerSchema </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> t.</span><span style="color:#50FA7B">Object</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    hello</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> t.</span><span style="color:#50FA7B">String</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> handler</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">body</span><span style="color:#F8F8F2"> }</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Context</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">typeof</span><span style="color:#F8F8F2"> HandlerSchema>) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { hello } </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> body;</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> app </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Elysia</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#6272A4">    // Type {} is missing the following properties from type unknown[]: length, pop, push, concat, and 28 more.</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, handler, {</span></span>
<span class="line"><span style="color:#F8F8F2">        body</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> HandlerSchema</span></span>
<span class="line"><span style="color:#F8F8F2">    })</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">listen</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">3000</span><span style="color:#F8F8F2">);</span></span></code></pre><p>To be honest, I used Claude for this question and discovered that I needed to access the inner <code>static</code> property for the type.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { </span><span style="color:#FF79C6">type</span><span style="color:#F8F8F2"> Context, Elysia, t } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">elysia</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> HandlerSchema </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> t.</span><span style="color:#50FA7B">Object</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    hello</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> t.</span><span style="color:#50FA7B">String</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">type</span><span style="color:#8BE9FD;font-style:italic"> Handler</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">typeof</span><span style="color:#F8F8F2"> HandlerSchema)[</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">static</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// TS2559: Type { hello: string; } has no properties in common with type RouteSchema</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> handler</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">body</span><span style="color:#F8F8F2"> }</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Context</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Handler</span><span style="color:#F8F8F2">>) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#6272A4">    // TS2339: Property hello does not exist on type unknown</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { hello } </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> body;</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Almost there, but the body type was still <code>unknown</code> and the generic wasn't correctly applied. After a few minutes of trial and error, I found the simple solution—the <code>body</code> key was missing.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { </span><span style="color:#FF79C6">type</span><span style="color:#F8F8F2"> Context, Elysia, t } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">elysia</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> HandlerSchema </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> t.</span><span style="color:#50FA7B">Object</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    hello</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> t.</span><span style="color:#50FA7B">String</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">type</span><span style="color:#8BE9FD;font-style:italic"> Handler</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">typeof</span><span style="color:#F8F8F2"> HandlerSchema)[</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">static</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> handler</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">body</span><span style="color:#F8F8F2"> }</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Context</span><span style="color:#F8F8F2">&#x3C;{ body</span><span style="color:#FF79C6">:</span><span style="color:#FFB86C;font-style:italic"> Handler</span><span style="color:#F8F8F2"> }>) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { hello } </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> body;</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> app </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Elysia</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, handler, {</span></span>
<span class="line"><span style="color:#F8F8F2">        body</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> HandlerSchema</span></span>
<span class="line"><span style="color:#F8F8F2">    })</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">listen</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">3000</span><span style="color:#F8F8F2">);</span></span></code></pre><p>And that's it—a properly typed, modularized handler.</p>
<h2>JWT</h2>
<p>Obviously, the fun didn't stop there. Later, I started using the <a href="https://elysiajs.com/plugins/jwt.html" rel="noopener noreferrer external">jwt</a> plugin, which added some complexity. Some type guards were also missing in the documentation.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { jwt } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@elysiajs/jwt</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Elysia } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">elysia</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> app </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Elysia</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">use</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#50FA7B">    jwt</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        name</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">jwt</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        secret</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">yolo</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    }).</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/profile</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">jwt</span><span style="color:#F8F8F2">, cookie: { </span><span style="color:#FFB86C;font-style:italic">auth</span><span style="color:#F8F8F2"> } }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#6272A4">        // TS2345: Argument of type unknown is not assignable to parameter of type string | undefined</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> profile </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> jwt.</span><span style="color:#50FA7B">verify</span><span style="color:#F8F8F2">(auth.value);</span></span>
<span class="line"><span style="color:#F8F8F2">    })</span></span>
<span class="line"><span style="color:#F8F8F2">);</span></span></code></pre><p>I added the necessary guards:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { jwt } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@elysiajs/jwt</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Elysia } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">elysia</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> app </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Elysia</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">use</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#50FA7B">    jwt</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        name</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">jwt</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        secret</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">yolo</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    }).</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/profile</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">jwt</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">status</span><span style="color:#F8F8F2">, cookie: { </span><span style="color:#FFB86C;font-style:italic">auth</span><span style="color:#F8F8F2"> } }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">auth) {</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#50FA7B"> status</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">401</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Unauthorized</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">auth.value) {</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#50FA7B"> status</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">401</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Unauthorized</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">typeof</span><span style="color:#F8F8F2"> auth.value </span><span style="color:#FF79C6">!==</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">string</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#50FA7B"> status</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">401</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Unauthorized</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> profile </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> jwt.</span><span style="color:#50FA7B">verify</span><span style="color:#F8F8F2">(auth.value);</span></span>
<span class="line"><span style="color:#F8F8F2">    })</span></span>
<span class="line"><span style="color:#F8F8F2">);</span></span></code></pre><p>Then I tried to modularize the handler as before (note: this is a GET request, so there's no body).</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { jwt } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@elysiajs/jwt</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { </span><span style="color:#FF79C6">type</span><span style="color:#F8F8F2"> Context, Elysia } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">elysia</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// TS2339: Property jwt does not exist on type</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> handler</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">jwt</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">status</span><span style="color:#F8F8F2">, cookie: { </span><span style="color:#FFB86C;font-style:italic">auth</span><span style="color:#F8F8F2"> } }</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Context</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">auth) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#50FA7B"> status</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">401</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Unauthorized</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">auth.value) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#50FA7B"> status</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">401</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Unauthorized</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">typeof</span><span style="color:#F8F8F2"> auth.value </span><span style="color:#FF79C6">!==</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">string</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#50FA7B"> status</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">401</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Unauthorized</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> profile </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> jwt.</span><span style="color:#50FA7B">verify</span><span style="color:#F8F8F2">(auth.value);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> app </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Elysia</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">use</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#50FA7B">    jwt</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        name</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">jwt</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        secret</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">yolo</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    }).</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/profile</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, handler)</span></span>
<span class="line"><span style="color:#F8F8F2">);</span></span></code></pre><p>To my surprise — though it should have been obvious since I had to install an additional plugin 😅 — <code>jwt</code> is not part of the core <code>Context</code>.</p>
<p>I spent some time searching through the <a href="https://github.com/elysiajs/elysia-jwt" rel="noopener noreferrer external">repo</a> but didn't find a clear answer. So I decided to extend the <code>Context</code> with a custom definition.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { jwt } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@elysiajs/jwt</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { </span><span style="color:#FF79C6">type</span><span style="color:#F8F8F2"> Context, Elysia } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">elysia</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">type</span><span style="color:#8BE9FD;font-style:italic"> JwtContext</span><span style="color:#FF79C6"> =</span><span style="color:#8BE9FD;font-style:italic"> Context</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    jwt</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">        sign</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">payload</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#50FA7B">        verify</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">jwt</span><span style="color:#FF79C6">?:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> false</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> handler</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">jwt</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">status</span><span style="color:#F8F8F2">, cookie: { </span><span style="color:#FFB86C;font-style:italic">auth</span><span style="color:#F8F8F2"> } }</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> JwtContext</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">auth) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#50FA7B"> status</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">401</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Unauthorized</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">auth.value) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#50FA7B"> status</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">401</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Unauthorized</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">typeof</span><span style="color:#F8F8F2"> auth.value </span><span style="color:#FF79C6">!==</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">string</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#50FA7B"> status</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">401</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Unauthorized</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> profile </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> jwt.</span><span style="color:#50FA7B">verify</span><span style="color:#F8F8F2">(auth.value);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> app </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Elysia</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">use</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#50FA7B">    jwt</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        name</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">jwt</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        secret</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">yolo</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#6272A4">        // The types of jwt.sign are incompatible between these types.</span></span>
<span class="line"><span style="color:#6272A4">        // ...</span></span>
<span class="line"><span style="color:#6272A4">        // is not assignable to type (payload: string) => Promise&#x3C;string></span></span>
<span class="line"><span style="color:#F8F8F2">    }).</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/profile</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, handler)</span></span>
<span class="line"><span style="color:#F8F8F2">);</span></span></code></pre><p>This resolved the <code>jwt</code> typing in the handler but led to a type mismatch error.</p>
<p>Fortunately, the jwt plugin code is relatively thin. I looked at the actual types for <a href="https://github.com/elysiajs/elysia-jwt/blob/c1ef8846bc6fbeee32ff3edacda8a7f281fc89db/src/index.ts#L240" rel="noopener noreferrer external">sign</a> and <a href="https://github.com/elysiajs/elysia-jwt/blob/c1ef8846bc6fbeee32ff3edacda8a7f281fc89db/src/index.ts#L364" rel="noopener noreferrer external">verify</a> and stripped them down to what I could import.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { jwt, </span><span style="color:#FF79C6">type</span><span style="color:#F8F8F2"> JWTPayloadInput, </span><span style="color:#FF79C6">type</span><span style="color:#F8F8F2"> JWTPayloadSpec } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@elysiajs/jwt</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { </span><span style="color:#FF79C6">type</span><span style="color:#F8F8F2"> Context, Elysia } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">elysia</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">type</span><span style="color:#8BE9FD;font-style:italic"> JwtContext</span><span style="color:#FF79C6"> =</span><span style="color:#8BE9FD;font-style:italic"> Context</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    jwt</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">        sign</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">payload</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> JWTPayloadInput</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#8BE9FD;font-style:italic"> Record</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">any</span><span style="color:#F8F8F2">>)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#50FA7B">        verify</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">jwt</span><span style="color:#FF79C6">?:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;(</span><span style="color:#FFB86C;font-style:italic">JWTPayloadSpec</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#FFB86C;font-style:italic"> Record</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">any</span><span style="color:#F8F8F2">>) </span><span style="color:#FF79C6">|</span><span style="color:#8BE9FD;font-style:italic"> false</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> handler</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">jwt</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">status</span><span style="color:#F8F8F2">, cookie: { </span><span style="color:#FFB86C;font-style:italic">auth</span><span style="color:#F8F8F2"> } }</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> JwtContext</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">auth) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#50FA7B"> status</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">401</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Unauthorized</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">auth.value) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#50FA7B"> status</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">401</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Unauthorized</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">typeof</span><span style="color:#F8F8F2"> auth.value </span><span style="color:#FF79C6">!==</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">string</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#50FA7B"> status</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">401</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Unauthorized</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> profile </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> jwt.</span><span style="color:#50FA7B">verify</span><span style="color:#F8F8F2">(auth.value);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> app </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Elysia</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">use</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#50FA7B">    jwt</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        name</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">jwt</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        secret</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">yolo</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    }).</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/profile</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, handler)</span></span>
<span class="line"><span style="color:#F8F8F2">);</span></span></code></pre><hr />
<h2>Putting It All Together</h2>
<p>Not five minutes later, I obviously needed to combine both solutions 😄.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { jwt, </span><span style="color:#FF79C6">type</span><span style="color:#F8F8F2"> JWTPayloadInput, </span><span style="color:#FF79C6">type</span><span style="color:#F8F8F2"> JWTPayloadSpec } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@elysiajs/jwt</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { </span><span style="color:#FF79C6">type</span><span style="color:#F8F8F2"> Context, Elysia, t } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">elysia</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> HandlerSchema </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> t.</span><span style="color:#50FA7B">Object</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    hello</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> t.</span><span style="color:#50FA7B">String</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">type</span><span style="color:#8BE9FD;font-style:italic"> JwtContext</span><span style="color:#FF79C6"> =</span><span style="color:#8BE9FD;font-style:italic"> Context</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    jwt</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">        sign</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">payload</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> JWTPayloadInput</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#8BE9FD;font-style:italic"> Record</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">any</span><span style="color:#F8F8F2">>)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#50FA7B">        verify</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">jwt</span><span style="color:#FF79C6">?:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;(</span><span style="color:#FFB86C;font-style:italic">JWTPayloadSpec</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#FFB86C;font-style:italic"> Record</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">any</span><span style="color:#F8F8F2">>) </span><span style="color:#FF79C6">|</span><span style="color:#8BE9FD;font-style:italic"> false</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> handler</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">jwt</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">status</span><span style="color:#F8F8F2">, cookie: { </span><span style="color:#FFB86C;font-style:italic">auth</span><span style="color:#F8F8F2"> }, </span><span style="color:#FFB86C;font-style:italic">body</span><span style="color:#F8F8F2"> }</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> JwtContext</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#6272A4">    // TS2339: Property hello does not exist on type unknown</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { hello } </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> body;</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> app </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Elysia</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">use</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#50FA7B">    jwt</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        name</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">jwt</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        secret</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">yolo</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    }).</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/profile</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, handler, {</span></span>
<span class="line"><span style="color:#F8F8F2">        body</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> HandlerSchema</span></span>
<span class="line"><span style="color:#F8F8F2">    })</span></span>
<span class="line"><span style="color:#F8F8F2">);</span></span></code></pre><p>I added my schema but lost the generic <code>Context</code> in the process, so I needed to make <code>JwtContext</code> support generics.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { jwt, </span><span style="color:#FF79C6">type</span><span style="color:#F8F8F2"> JWTPayloadInput, </span><span style="color:#FF79C6">type</span><span style="color:#F8F8F2"> JWTPayloadSpec } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@elysiajs/jwt</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { </span><span style="color:#FF79C6">type</span><span style="color:#F8F8F2"> Context, Elysia, t } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">elysia</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> HandlerSchema </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> t.</span><span style="color:#50FA7B">Object</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    hello</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> t.</span><span style="color:#50FA7B">String</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">type</span><span style="color:#8BE9FD;font-style:italic"> Handler</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">typeof</span><span style="color:#F8F8F2"> HandlerSchema)[</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">static</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// TS2344: Type Body does not satisfy the constraint RouteSchema</span></span>
<span class="line"><span style="color:#FF79C6">type</span><span style="color:#8BE9FD;font-style:italic"> JwtContext</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Body</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=</span><span style="color:#8BE9FD;font-style:italic"> Context</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Body</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">&#x26;</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    jwt</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">        sign</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">payload</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> JWTPayloadInput</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#8BE9FD;font-style:italic"> Record</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">any</span><span style="color:#F8F8F2">>)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#50FA7B">        verify</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">jwt</span><span style="color:#FF79C6">?:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;(</span><span style="color:#FFB86C;font-style:italic">JWTPayloadSpec</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#FFB86C;font-style:italic"> Record</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">any</span><span style="color:#F8F8F2">>) </span><span style="color:#FF79C6">|</span><span style="color:#8BE9FD;font-style:italic"> false</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> handler</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">jwt</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">status</span><span style="color:#F8F8F2">, cookie: { </span><span style="color:#FFB86C;font-style:italic">auth</span><span style="color:#F8F8F2"> }, </span><span style="color:#FFB86C;font-style:italic">body</span><span style="color:#F8F8F2"> }</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> JwtContext</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Handler</span><span style="color:#F8F8F2">>) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#6272A4">    // TS2339: Property hello does not exist on type unknown</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { hello } </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> body;</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> app </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Elysia</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">use</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#50FA7B">    jwt</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        name</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">jwt</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        secret</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">yolo</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    }).</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/profile</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, handler, {</span></span>
<span class="line"><span style="color:#F8F8F2">        body</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> HandlerSchema</span></span>
<span class="line"><span style="color:#F8F8F2">    })</span></span>
<span class="line"><span style="color:#F8F8F2">);</span></span></code></pre><p>First, I fixed the generic constraint by looking at the parent definition—it needed the <code>RouteSchema</code> constraint and a default value.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">type</span><span style="color:#8BE9FD;font-style:italic"> JwtContext</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Body</span><span style="color:#FF79C6"> extends</span><span style="color:#FFB86C;font-style:italic"> RouteSchema</span><span style="color:#FF79C6"> =</span><span style="color:#FFB86C;font-style:italic"> RouteSchema</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=</span><span style="color:#8BE9FD;font-style:italic"> Context</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Body</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">&#x26;</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    jwt</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">        sign</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">payload</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> JWTPayloadInput</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#8BE9FD;font-style:italic"> Record</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">any</span><span style="color:#F8F8F2">>)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#50FA7B">        verify</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">jwt</span><span style="color:#FF79C6">?:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;(</span><span style="color:#FFB86C;font-style:italic">JWTPayloadSpec</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#FFB86C;font-style:italic"> Record</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">any</span><span style="color:#F8F8F2">>) </span><span style="color:#FF79C6">|</span><span style="color:#8BE9FD;font-style:italic"> false</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// TS2559: Type { hello: string; } has no properties in common with type RouteSchema</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> handler</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">jwt</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">status</span><span style="color:#F8F8F2">, cookie: { </span><span style="color:#FFB86C;font-style:italic">auth</span><span style="color:#F8F8F2"> }, </span><span style="color:#FFB86C;font-style:italic">body</span><span style="color:#F8F8F2"> }</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> JwtContext</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Handler</span><span style="color:#F8F8F2">>) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#6272A4">    // TS2339: Property hello does not exist on type unknown</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { hello } </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> body;</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Getting closer. I then realized I was trying to provide the type of my schema for the entire route instead of defining only the body.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> handler</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">jwt</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">status</span><span style="color:#F8F8F2">, cookie: { </span><span style="color:#FFB86C;font-style:italic">auth</span><span style="color:#F8F8F2"> }, </span><span style="color:#FFB86C;font-style:italic">body</span><span style="color:#F8F8F2"> }</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> JwtContext</span><span style="color:#F8F8F2">&#x3C;{ body</span><span style="color:#FF79C6">:</span><span style="color:#FFB86C;font-style:italic"> Handler</span><span style="color:#F8F8F2"> }>) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { hello } </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> body;</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Perfect! This resolved all the issues. One final tweak — renaming the generic parameter from <code>Body</code> to <code>Route</code> since it represents the entire route schema, not just the body.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { jwt, </span><span style="color:#FF79C6">type</span><span style="color:#F8F8F2"> JWTPayloadInput, </span><span style="color:#FF79C6">type</span><span style="color:#F8F8F2"> JWTPayloadSpec } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@elysiajs/jwt</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { </span><span style="color:#FF79C6">type</span><span style="color:#F8F8F2"> Context, Elysia, </span><span style="color:#FF79C6">type</span><span style="color:#F8F8F2"> RouteSchema, t } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">elysia</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> HandlerSchema </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> t.</span><span style="color:#50FA7B">Object</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    hello</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> t.</span><span style="color:#50FA7B">String</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">type</span><span style="color:#8BE9FD;font-style:italic"> Handler</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">typeof</span><span style="color:#F8F8F2"> HandlerSchema)[</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">static</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">type</span><span style="color:#8BE9FD;font-style:italic"> JwtContext</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Route</span><span style="color:#FF79C6"> extends</span><span style="color:#FFB86C;font-style:italic"> RouteSchema</span><span style="color:#FF79C6"> =</span><span style="color:#FFB86C;font-style:italic"> RouteSchema</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=</span><span style="color:#8BE9FD;font-style:italic"> Context</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Route</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">&#x26;</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    jwt</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">        sign</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">payload</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> JWTPayloadInput</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#8BE9FD;font-style:italic"> Record</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">any</span><span style="color:#F8F8F2">>)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#50FA7B">        verify</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">jwt</span><span style="color:#FF79C6">?:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;(</span><span style="color:#FFB86C;font-style:italic">JWTPayloadSpec</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#FFB86C;font-style:italic"> Record</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">any</span><span style="color:#F8F8F2">>) </span><span style="color:#FF79C6">|</span><span style="color:#8BE9FD;font-style:italic"> false</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> handler</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">jwt</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">status</span><span style="color:#F8F8F2">, cookie: { </span><span style="color:#FFB86C;font-style:italic">auth</span><span style="color:#F8F8F2"> }, </span><span style="color:#FFB86C;font-style:italic">body</span><span style="color:#F8F8F2"> }</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> JwtContext</span><span style="color:#F8F8F2">&#x3C;{ body</span><span style="color:#FF79C6">:</span><span style="color:#FFB86C;font-style:italic"> Handler</span><span style="color:#F8F8F2"> }>) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { hello } </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> body;</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> app </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Elysia</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">use</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#50FA7B">    jwt</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        name</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">jwt</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        secret</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">yolo</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    }).</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/profile</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, handler, {</span></span>
<span class="line"><span style="color:#F8F8F2">        body</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> HandlerSchema</span></span>
<span class="line"><span style="color:#F8F8F2">    })</span></span>
<span class="line"><span style="color:#F8F8F2">);</span></span></code></pre><p>And voilà, problem solved 👨‍🍳.</p>
<hr />
<h2>Conclusion</h2>
<p>I've been experimenting with Bun and Elysia recently and really enjoying the experience. The solutions above are working well for my use case, though I'm still searching for one thing: a way to validate objects strictly.</p>
<p>Similar to Zod's strict mode, I'd love something like <code>t.strictObject()</code> that rejects additional properties:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> app </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Elysia</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, handler, {</span></span>
<span class="line"><span style="color:#6272A4">        // TS2339: Property strictObject does not exist on type</span></span>
<span class="line"><span style="color:#F8F8F2">        body</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> t.</span><span style="color:#50FA7B">strictObject</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">            hello</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> t.</span><span style="color:#50FA7B">String</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">        })</span></span>
<span class="line"><span style="color:#F8F8F2">    })</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">listen</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">3000</span><span style="color:#F8F8F2">);</span></span></code></pre><p>If you know how to achieve this in Elysia, please reach out!</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Wrapped (2025) & Onward]]></title>
          <description><![CDATA[Extending collaboration, Juno's reality check, and some ideas for 2026.]]></description>
          <link>https://daviddalbusco.com/blog/wrapped-2025-and-onward/</link>
          <pubDate>Fri, 19 Dec 2025 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/rene-bohmer-YeUVDKZWSZ4-unsplash.webp" alt="Cover photo from Unsplash by Rene Böhmer" / loading="lazy"></p>
<blockquote>
<p>Photo by <a href="https://unsplash.com/fr/@qrenep?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Rene Böhmer</a> on <a href="https://unsplash.com/fr/photos/a-very-long-line-of-yellow-lines-on-a-black-background-YeUVDKZWSZ4?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></p>
</blockquote>
<p><strong>TLDR</strong>:</p>
<ul>
<li>I'll likely be available for freelance hire as of May 2026</li>
<li>The DFINITY Foundation is still sponsoring Juno for the next few months</li>
</ul>
<hr />
<p>My life isn't particularly interesting, but since a few people are using <a href="https://juno.build" rel="noopener noreferrer external">Juno</a>, the platform I'm developing, I figured now that things are finally clear — and square, psycho-rigid old guy here — I should reflect on the year that passed and share what comes next.</p>
<p>Long story short: the <a href="https://dfinity.org/" rel="noopener noreferrer external">DFINITY Foundation</a> and I have agreed to extend our collaboration until the end of April 2026. This year, my main task was improving the <a href="https://internetcomputer.org/" rel="noopener noreferrer external">Internet Computer</a> ecosystem and adoption by developing Juno. The upcoming period shifts focus—I'll be building things and providing consulting for them.</p>
<p>The good news for you: while no longer the primary focus and the sponsorship is reduced, the Foundation will continue supporting Juno and I'm really grateful for it.</p>
<p>I'm obviously biased, but this makes sense (to me at least): I'm reaching developers outside the blockchain bubble (especially trying to reach those who hate crypto), and they're actually using Juno for their own pipelines and projects (like the <a href="https://javascript.icp.build" rel="noopener noreferrer external">JavaScript SDK documentation</a>). Not bad to have me around if you're using my tools 😄.</p>
<hr />
<h2>What Happens After?</h2>
<p>At this point however, you might be asking yourself: yeah that's great David, but what happens after April then?</p>
<p>Let me be honest: without renewed sponsorship or wider adoption, Juno might likely enter some sort of maintenance mode.</p>
<p>The platform hasn't reached the massive adoption needed to be financially self-sustainable yet. And I have little interest in convincing VCs — I'm not great at that dance, and frankly, I don't want to deal with investors who don't get it.</p>
<p>Of course, it's startup life—ups and downs. Right now I'm maybe more in a down, doubting whether I'll ever reach the masses.</p>
<p>Because let's be frank: Juno is a great alternative, but it's a little fish in a crowded, established market. Developers already have solutions that work for them. Convincing them to try something fundamentally different (even if the DX is relatively similar), especially when it's built on different infrastructure with likely fewer features (currently), is an uphill battle.</p>
<p>Plus, I almost completely failed to catch the AI wave (not that I really tried) and more generally, I feel like devs aren't interested in changing their habits. Vercel had two major security incidents and their CEO befriended a war criminal (I'm talking about Guillermo Rauch <a href="https://x.com/rauchg/status/1972669025525158031" rel="noopener noreferrer external">taking selfies</a> with Netanyahu) — nothing changed. Habits die hard.</p>
<p>So yeah, it's tough work, I might not be doing it always right, but I'll continue doing my best to push for the next few months. We'll see where it lands — hopefully on the moon! 🚀</p>
<hr />
<h2>By the Numbers</h2>
<p>For once, let's not get too technical — you can find everything that was released on Juno this year on the <a href="https://juno.build/blog" rel="noopener noreferrer external">website's blog</a> — but rather let me share some numbers that are a bit more juicy.</p>
<h3>Sign-ups</h3>
<p>Since February 2023 — damn, almost three years already — <strong>2,410</strong> potential developers have signed into Juno's Console. I purposely don't track retention rate, meaning I don't know exactly how many of those really built something with it, but I'd estimate the number is low, maybe 2% or something.</p>
<p>That said, my estimation might be too pessimistic as I'm more a glass-half-empty guy on that matter. There are some double-digit metrics I can't disclose that are actually more positive and validate the fact that Juno as a hosting provider has found its market fit, at least on the Internet Computer.</p>
<p>On average, I estimate new monthly sign-ups to be between <strong>60-100</strong>. It strongly depends on how active I am in terms of promotion.</p>
<p>One interesting thing though: this year I (finally) introduced native <a href="https://juno.build/blog/google-sign-in-comes-to-juno" rel="noopener noreferrer external">Google sign-in</a>. As a result, one number speaks for itself: I introduced this method on Oct. 25, and since then there were <strong>83 sign-ups</strong> with Internet Identity versus... <strong>75 with Google</strong>.</p>
<p>The number of sign-ins with Google is about to overtake the majority. This confirms this was an interesting move and opens up some possibilities for the future.</p>
<h3>Positioning</h3>
<p>In terms of SEO, I have to say it could be improved. We — a friend of mine is helping with marketing — invested some time and effort to refine our approach and it's starting to improve, but it's a process that takes time.</p>
<p>On that particular topic, one important move this year was deprecating and removing as much as possible crypto, blockchain, web3, and other mumbo jumbo slang. The target for Juno, the hope, is reaching developers who have no interest, clue, or even dislike that field. So I refined the message multiple times and continue to do so. That's how I landed on a tagline and description that say:</p>
<blockquote>
<p>Build serverless apps with self-hosting control<br />
Juno is an open-source serverless platform to build, deploy, and run apps in WASM containers with complete ownership and zero DevOps</p>
</blockquote>
<p>This refined message and unleashing Google sign-in unlocked new opportunities. Thanks to it, I was able to start featuring the platform in various aggregated lists or documentation related to web developer tools that aren't necessarily related to or blockchain-friendly. Hopefully those backlinks will start to shine at some point!</p>
<h3>Community</h3>
<p>When it comes to following, there are <strong>1,400+</strong> followers on Twitter and <strong>230</strong> members on Discord. It's maybe not as active as I wish it could be, but I definitely feel like there's a little supportive community and it's great to have everybody around 💙.</p>
<h3>Open Source</h3>
<p>GitHub stars wise, Juno is still not that famous a project — not that stars are an obsession, but they do disclose how trendy something is. Interesting fact though: it's the most starred <a href="https://github.com/topics/internet-computer" rel="noopener noreferrer external">internet-computer</a> project that is not built by the Foundation.</p>
<p>There was a nice push in September, so hopefully it will continue that way. And if you haven't yet starred Juno, now is the time, old man or woman 😉.</p>
<p>👉 <a href="https://github.com/junobuild/juno" rel="noopener noreferrer external">https://github.com/junobuild/juno</a></p>
<p><img src="https://daviddalbusco.com/assets/images/20251219-juno-github-stars.png" alt="343 stars on Github on Dec 19, 2025" / loading="lazy"></p>
<hr />
<h2>My Ideas for 2026</h2>
<p>I can't build everything — reduced sponsorship means reduced resources — but I'll try to ship at least one of these (probably team support) in the next few months.</p>
<h3>GitHub Integration</h3>
<p>GitHub integration was actually one of my goals for 2025. Not just GitHub Actions, but deep integration including sign-in, automatic deployment through GitHub Apps, etc.</p>
<p>Despite some proof of concepts and analysis, there wasn't a clean solution without compromising too much on either ownership or security (or putting too much responsibility on my shoulders). That is why it did not happen yet.</p>
<p>However, things have changed. Implementing Google sign-in meant building OpenID support. GitHub still only uses OAuth, which can't be integrated directly for the reasons above, but I think I might be able to work around this with a proxy server (that anyone can validate). It's just an idea at this point, but I think there is an opportunity here.</p>
<p>If I want to compete with traditional dev experience, better GitHub integration is a must. And by &quot;better integration,&quot; I don't mean an &quot;export&quot; button like some platforms do—I mean real integration with as little compromises on values as possible.</p>
<h3>Team</h3>
<p>More and more teams, small and large, are using my platform. There are workarounds to share projects, but it's not officially supported. Since the Foundation is both using Juno and requires team features, I see some synergy here.</p>
<p>Moreover, I'm currently working on big changes to merge Mission Control and monitoring tools into a single umbrella or in other words, I'm making the platform even more microservices-oriented. Implementing team support fits that narrative well.</p>
<h3>SQL</h3>
<p>I'll cut it short: I don't think SQL is the silver bullet that will suddenly make Juno massively adopted. However, the current datastore approach definitely has its requirements and limitations. It has advantages too, but providing devs a more familiar database-like experience would be really useful and can unlock some ideas.</p>
<p>When I started Juno three years ago, I actually wished I could have used that technology, but it wasn't there yet. This year, some new crates were published on the IC, and even <a href="https://github.com/rusqlite/rusqlite/pull/1769#issuecomment-3650506988" rel="noopener noreferrer external">rusqlite</a> became compatible just last week. That's why it would now be possible to move in that direction.</p>
<p>That said, I'm well aware of the security implications, and there's no way I would introduce anything insecure or unmaintainable.</p>
<p>But think about it: once you have SQL with a nice GUI on top, together with all of Juno — that would be quite awesome.</p>
<h3>Self-Hosting</h3>
<p>Last but personally not least, I'd like to improve self-hosting. Not just allowing devs to run projects with privacy and self-hosting control, but also letting devs and companies host their own version of Juno.</p>
<p>Think about it — you wouldn't just develop your project, but you'd also have your own instance of the platform and all the tooling.</p>
<p>I already did this to some extent this year — the local emulator now ships the entire Juno. Going a step forward and offering that level of control and self-hosting for production would be really slick, in my opinion.</p>
<hr />
<h2>Thanks &amp; Onward</h2>
<p>Before I wrap this up, I want to thank everyone who's been supporting, using, promoting, or helping <a href="https://juno.build" rel="noopener noreferrer external">Juno</a> this year — it means a lot! I hope you'll continue to stick around through 2026, or at least the next few months.</p>
<p>The path forward is uncertain but, we have some buffers and rebellions are made of hope, aren't they?</p>
<p>I wish you a merry Christmas and a lovely end of year! 🎄</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Hack the World (Computer)]]></title>
          <description><![CDATA[How I used a CSP loophole to rescue trapped ICP tokens from a web app]]></description>
          <link>https://daviddalbusco.com/blog/hack-the-world-computer/</link>
          <pubDate>Thu, 27 Nov 2025 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*I8hVM9GzLEmHtVsAr2G4zA.jpeg" alt="" / loading="lazy"></p>
<blockquote>
<p>Photo by <a href="https://unsplash.com/fr/@markusspiske?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Markus Spiske</a> on <a href="https://unsplash.com/fr/photos/photo-du-film-matrix-iar-afB0QQw?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></p>
</blockquote>
<p><strong>TLDR</strong>: I obviously did not hack anything but, I may have found a creative way to inject and run code in a web app to resolve an issue. So here's a little story...</p>
<hr />
<h2>The Context</h2>
<p>On the World Computer, or the <a href="https://internetcomputer.org/" rel="noopener noreferrer external">Internet Computer</a>, a public decentralized network which runs WASM containers that can serve web applications and sites, anytime you sign-in in an app you get a session (identity) that is identified by an anonymous non-trackable unique ID (principal, basically a public key) which happens to be a wallet address as well.</p>
<p>In other words, it means that wherever you authenticate, you basically get each time kind of an IBAN number which can - or not - be used to hold money, crypto ICP in that case.</p>
<p>That's an interesting pattern and challenge as you might end up having assets a bit everywhere - though nowadays the integration of well known wallets such as <a href="https://oisy.com/" rel="noopener noreferrer external">OISY</a> and established standards limits a bit the &quot;issue&quot;.</p>
<p>However, particularly if you do not pay attention or if the app you are using does not offer a great UI/UX, you might send some money to the wrong address. As for example if you copied your address x but thought you had copied your address y.</p>
<p>This isn't a problem per se unless you send ICP to an incorrect address (that you control) but finds place in an app that does not offer possibilities to get the money out.</p>
<hr />
<h2>The Problem</h2>
<p>So a few days ago a friend was facing exactly this situation. He was staring at a situation where he'd potentially lost money with no way to get it out.</p>
<p>He started looking for help and asked if anyone had an idea how to work around this.</p>
<hr />
<h2>The Memory</h2>
<p>Since I've been active in the community for a few years, it turned out this wasn't my first rodeo. Few years ago, I'd actually developed a script after one or two beers 😅 at like midnight to help someone in the community resolving a similar issue as soon as possible. I exactly remember writing it in my hotel room while attending the <a href="https://www.wearedevelopers.com/" rel="noopener noreferrer external">WeAreDevelopers</a> conference to give a workshop about <a href="https://juno.build" rel="noopener noreferrer external">Juno</a>.</p>
<p>The idea of the workaround is pretty straightforward: leverage the browser's debugger to run a script in the console after the user has signed in. This script loads the session (identity) the same way the app itself does, then initializes and executes functions that aren't actually implemented by the app.</p>
<p>But I wasn't sure if that &quot;hack&quot; was still working, so first I had to try it.</p>
<hr />
<h2>First Attempt: Blocked by CSP</h2>
<p>I opened the website (as good hobbyist hacker here I obviously keep its name obfuscated) and launched my import:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> { transfer } </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#FF79C6"> import</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://unpkg.com/yolosns@latest/dist/esm/index.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span></code></pre><p>Unfortunately, or fortunately if you like me rather like security, this did not work out as it was blocked by the CSP:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">VM50:</span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2"> Loading the script </span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">https://unpkg.com/yolosns@latest/dist/esm/index.js</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2"> violates the following Content Security Policy directive: </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval'</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">. Note that </span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">script-src-elem</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2"> was not explicitly set, so </span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">script-src</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2"> is used </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> a</span><span style="color:#8BE9FD;font-style:italic"> fallback</span><span style="color:#F8F8F2">. </span><span style="color:#8BE9FD;font-style:italic">The</span><span style="color:#8BE9FD;font-style:italic"> action</span><span style="color:#8BE9FD;font-style:italic"> has</span><span style="color:#8BE9FD;font-style:italic"> been</span><span style="color:#8BE9FD;font-style:italic"> blocked</span><span style="color:#F8F8F2">.</span></span>
<span class="line"><span style="color:#F8F8F2">Uncaught TypeError: Failed to fetch dynamically imported module: https:</span><span style="color:#6272A4">//unpkg.com/yolosns@latest/dist/esm/index.js</span></span></code></pre><p><img src="https://daviddalbusco.com/assets/images/1*7dev_oxGbi42GNjlwg9r_g.png" alt="A screenshot of the import blocked by the CSP in the browser console debugger" / loading="lazy"></p>
<p>At this point I was about to share that it couldn't work but, then I told myself, wait have a second look.</p>
<hr />
<h2>The Loophole</h2>
<p>I opened the elements, found no CSP defined there, then checked the network tab and figured out those were nicely set using HTTP headers in the response:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">default-</span><span style="color:#F8F8F2">src </span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">none</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">; script</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">src </span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">self</span><span style="color:#E9F284">'</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">wasm-unsafe-eval</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">; worker</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">src </span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">self</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2"> blob:; connect</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">src </span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">self</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2"> https:</span><span style="color:#6272A4">//icp-api.io https://ic0.app https://icp0.io https://3r4gx-wqaaa-aaaaq-aaaia-cai.icp0.io; img-src 'self' data:; font-src 'self'; object-src 'none'; base-uri 'self'; style-src 'self' 'unsafe-inline'; media-src 'self' data: blob:; form-action 'self'; frame-ancestors 'none'; upgrade-insecure-requests</span></span></code></pre><p>From a quick look, everything seemed tight but, then I noticed something. The <code>worker-src</code>, I assume the app is using web workers, is accepting <code>blob:</code>.</p>
<p>It hit me and I asked myself: but wait, can I actually create a web worker on the fly and run it?</p>
<p>I started to search how you can create a web worker from a blob and found the solution on <a href="https://stackoverflow.com/a/61621269/5404186" rel="noopener noreferrer external">Stack Overflow</a>.</p>
<p>I gave it a try and it worked out, the console log was executed:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> code </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">console.log('Hello from web worker!')</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> blob </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Blob</span><span style="color:#F8F8F2">([code], { type</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">application/javascript</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> });</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> worker </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Worker</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">URL</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">createObjectURL</span><span style="color:#F8F8F2">(blob));</span></span></code></pre><p>At this point I knew it was possible to inject a script but, I still had to figure out how to inject one that actually does something and how to create the blob.</p>
<p>I was also aware that creating the blob with a plain text would (maybe) be tricky or not that handy, just thinking at dealing with the quotes was already a no go in my mind.</p>
<hr />
<h2>Building the Solution</h2>
<p>I didn't have to search too long. I'd recently developed a new feature for Juno's CLI: <code>juno run</code></p>
<p>It's a command that runs arbitrary scripts in the CLI context. Super handy for running anything, and it even works in GitHub Actions - but I'm getting off track, that's for another blog post.</p>
<p>Anyway, it works similarly. The CLI compiles the script using <code>esbuild</code>, converts the output from <code>Uint8Array</code> to <code>base64</code>, and executes it using an <code>await import</code>.</p>
<p>Pretty much the same technique.</p>
<p>So I started working on the solution. First, I created a test script - the one I wanted to inject and run:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Yolo</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span></code></pre><p>Then I created a build script to compile the code and prepare it for manual transport into the browser.</p>
<p>I started with the esbuild part. I use <a href="https://esbuild.github.io/" rel="noopener noreferrer external">esbuild</a> everywhere, big fan, I basically just had to copy/paste my own recipe:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> { outputFiles } </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> esbuild.</span><span style="color:#50FA7B">build</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    entryPoints</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">src/transfer.ts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">    write</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    bundle</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    minify</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    splitting</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    treeShaking</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    format</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">esm</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    define</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { global</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">window</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> },</span></span>
<span class="line"><span style="color:#F8F8F2">    target</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">esnext</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">    platform</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">browser</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    conditions</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">worker</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">browser</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">]</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> code </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> outputFiles?.[</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">]?.contents;</span></span></code></pre><p>I set <code>write</code> to <code>false</code> to not generate the output into a file and no <code>splitting</code> or <code>treeshaking</code> as I'm looking for a single JS file. I use esm because, well, it's 2025 and add a <code>conditions</code> worker. Don't remember from where I got that, but I knew and know it works.</p>
<p>Esbuild provides back an object which contains the code as <code>Uint8Array</code>.</p>
<p>Copying/pasting an array is inconvenient, plain string as I said would be not handy so I converted the code to <code>base64</code>:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> base64 </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> Buffer.</span><span style="color:#50FA7B">from</span><span style="color:#F8F8F2">(code).</span><span style="color:#50FA7B">toString</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`base64`</span><span style="color:#F8F8F2">);</span></span></code></pre><p>Finally I wrote the output to a plain file because printing to the terminal the final solution likely would be too long:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">writeFileSync</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">join</span><span style="color:#F8F8F2">(process.</span><span style="color:#50FA7B">cwd</span><span style="color:#F8F8F2">(), </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">script.txt</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">), base64);</span></span></code></pre><p>Great potato script but, does the job. Run against my test script I got:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">Y29uc29sZS5sb2coIllvbG8iKTsK</span></span></code></pre><p>How cute.</p>
<hr />
<h2>Testing the Flow</h2>
<p>So I reloaded the web app and tested the complete flow with my base64-encoded test script:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> code </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Y29uc29sZS5sb2coIllvbG8iKTsK</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> uint8Array </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> Uint8Array.</span><span style="color:#50FA7B">fromBase64</span><span style="color:#F8F8F2">(code);</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> blob </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Blob</span><span style="color:#F8F8F2">([uint8Array], { type</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">application/javascript</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> });</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> worker </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Worker</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">URL</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">createObjectURL</span><span style="color:#F8F8F2">(blob));</span></span></code></pre><p><img src="https://daviddalbusco.com/assets/images/1*61rGpu9ntwL3sImWmD880w.png" alt="A screenshot of the step successfully run in the Chrome browser debugger" / loading="lazy"></p>
<p>Hooray that did the trick!!! The console logged &quot;Yolo&quot;. 🥳</p>
<hr />
<h2>The Transfer Script</h2>
<p>From there, it was just typical Internet Computer code as you would do in an app.</p>
<p>First, a script to create an authentication client - i.e. a client that reads the session key:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { AuthClient } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@icp-sdk/auth/client</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> createAuthClient</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">AuthClient</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=></span></span>
<span class="line"><span style="color:#F8F8F2">    AuthClient.</span><span style="color:#50FA7B">create</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        idleOptions</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">            disableIdle</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">            disableDefaultIdleCallback</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span></code></pre><p>Then creating what we call an agent - a transport layer that takes care of executing queries and calls for you using the session and a specified network, in this case mainnet:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { AuthClient } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@icp-sdk/auth/client</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { HttpAgent } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@icp-sdk/core/agent</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Principal } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@icp-sdk/core/principal</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> createClient</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;{</span></span>
<span class="line"><span style="color:#F8F8F2">    agent</span><span style="color:#FF79C6">:</span><span style="color:#FFB86C;font-style:italic"> HttpAgent</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    user</span><span style="color:#FF79C6">:</span><span style="color:#FFB86C;font-style:italic"> Principal</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}> </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> authClient </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> createAuthClient</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> agent </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> HttpAgent.</span><span style="color:#50FA7B">create</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        host</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">https://icp-api.io</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        identity</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> authClient.</span><span style="color:#50FA7B">getIdentity</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        agent,</span></span>
<span class="line"><span style="color:#F8F8F2">        user</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> authClient.</span><span style="color:#50FA7B">getIdentity</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">getPrincipal</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>And finally putting together the function that effectively calls the ICP ledger for the session - basically the operation that tells the ledger to transfer the money somewhere else:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { IcrcLedgerCanister } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@icp-sdk/canisters/ledger/icrc</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> transfer</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ({</span></span>
<span class="line"><span style="color:#F8F8F2">    ledgerCanisterId,</span></span>
<span class="line"><span style="color:#F8F8F2">    owner,</span></span>
<span class="line"><span style="color:#F8F8F2">    amount,</span></span>
<span class="line"><span style="color:#F8F8F2">    fee</span></span>
<span class="line"><span style="color:#F8F8F2">}</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    ledgerCanisterId</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    owner</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    amount</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> bigint</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    fee</span><span style="color:#FF79C6">?:</span><span style="color:#8BE9FD;font-style:italic"> bigint</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { agent } </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> createClient</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { transfer } </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> IcrcLedgerCanister.</span><span style="color:#50FA7B">create</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        agent,</span></span>
<span class="line"><span style="color:#F8F8F2">        canisterId</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> Principal.</span><span style="color:#50FA7B">fromText</span><span style="color:#F8F8F2">(ledgerCanisterId)</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#50FA7B"> transfer</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        to</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { owner</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> Principal.</span><span style="color:#50FA7B">fromText</span><span style="color:#F8F8F2">(owner), subaccount</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [] },</span></span>
<span class="line"><span style="color:#F8F8F2">        amount,</span></span>
<span class="line"><span style="color:#F8F8F2">        fee</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>I've done that zillion times so it took me likely less than a minute to set up. Ultimately, I ended the script with the effective usage wrapped as a function because I think I couldn't execute an asynchronous call at the root of the worker:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">************** Hello transfer ****************</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#50FA7B"> transfer</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        ledgerCanisterId</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">ryjl3-tyaaa-aaaaa-aaaba-cai</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        owner</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">DESTINATION_ACCOUNT_PRINCIPAL</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        amount</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 1234</span><span style="color:#FF79C6">n</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">})();</span></span></code></pre><p>And voilà. I committed everything in a repo and sent it to my friend, telling him he should edit the script with the address he would like to retrieve his funds to and then provided the step by step instruction and he got his money back. ✅</p>
<hr />
<p>I'm definitely not a hacker and this ain't a hack but, it definitely felt like I was one! 😃</p>
<p>Hope you found the little story entertaining. Until next time.</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Transruinaulta]]></title>
          <link>https://daviddalbusco.com/trails/2025/transruinaulta/</link>
          <pubDate>Sat, 18 Oct 2025 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>My third participation in the Transruinaulta trail marathon - such an amazing event and the perfect way to wrap up my summer distance running adventure!</p>
<p>Fun fact: even though it felt like I pushed harder than in previous years, I somehow finished with almost the exact same time as last year 🤷‍♂️</p>
<p>And the weather couldn’t have been better ☀️</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[TRE Einsiedler]]></title>
          <link>https://daviddalbusco.com/trails/2025/tre-einsiedler/</link>
          <pubDate>Sun, 05 Oct 2025 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>12/10 would recommend participating in the Trail Run Einsiedeln.</p>
<p>👉 https://trail-maniacs.ch/trail-run-einsiedeln/</p>
<p>Minimum essentials, great adventure!<br />
Grit, Grind &amp; Good Vibes!</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Stans - Wirzweli]]></title>
          <link>https://daviddalbusco.com/trails/2025/stans-wirzweli/</link>
          <pubDate>Wed, 10 Sep 2025 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>Easy run from Stans to Wirzweli, going up the Stanserhorn and the Gräfimattstand.</p>
<p>In Wirzweli, I took the cable car down. It’s probably possible to run down to Dallenwil to continue the trail, but I figured it would be nice to take the car at least once this year, today being likely the last day of summer 😉</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Parpaner Rothorn (short)]]></title>
          <link>https://daviddalbusco.com/trails/2024/parpaner-rothorn-short/</link>
          <pubDate>Mon, 08 Sep 2025 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>My second time at Parpaner Rothorn, I took a different route, choosing the shorter FHGR Trail Run, which saves 7 km and 400 meters of ascent.</p>
<p>The route should have been 26 km, but for some reason, I lost the path quite often. You might want to download the official GPX data for a cleaner path.</p>
<p>The trail was a bit more crowded than two years ago, and unlike my previous visit, I didn’t see any marmots on the way down. The weather was super windy, just like the first time, so I guess this spot is particularly prone to wind.</p>
<hr />
<p>Waypoints:</p>
<ul>
<li>Lantsch/Lenz, Biathlon Arena (1387m)</li>
<li>Bergrestaurant Scharmoin (7.9km, 1902m)</li>
<li>Hörnli Berg - Hörnlihütte Arosa (14.2km, 2493m)</li>
<li>Panoramarestaurant Rothorngipfel (22km, 2856m)</li>
<li>Alp Sanaspan (26.6km, 2000m)</li>
<li>Lantsch/Lenz, Biathlon Arena (1387m)</li>
</ul>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Klosters]]></title>
          <link>https://daviddalbusco.com/trails/2025/klosters/</link>
          <pubDate>Sun, 07 Sep 2025 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>I was pretty slow on this trail – had a “hard” time figuring out the way, and the terrain was slippery, so I couldn’t really get much speed on the downhills. But aside from that, this loop is absolutely stunning. You can start and finish in Klosters Platz (I ended in Klosters Dorf for simplicity), and the mix of views and wildness is just amazing.</p>
<p>My track isn’t super clean, so if you want to explore it yourself I’d recommend grabbing the official Madrisa Trail route (which I followed) – or even better, join the event next summer: https://www.madrisatrail.ch/t33-trail/</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Appenzeller Alpenweg]]></title>
          <link>https://daviddalbusco.com/trails/2025/appenzeller-alpenweg/</link>
          <pubDate>Fri, 15 Aug 2025 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>Ran the Appenzeller Alpenweg again today, one of my easy-going standard runs. No particular difficulties, really easy, and pleasantly uncrowded during the week.</p>
<p>I wouldn't recommend following my exact trace though, I lost track once or twice and had to add some extra meters due to a road closure for woodwork.</p>
<p>Better option: check out the official route of these two day-hikes combined into one day’s trail on their website.</p>
<p>👉 https://appenzellerland.ch/de/erleben/wandern-bewegen/sommer/wanderrouten/appenzeller-alpenweg.html</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Jenaz - Glattwang]]></title>
          <link>https://daviddalbusco.com/trails/2025/jenaz-glattwang/</link>
          <pubDate>Sat, 09 Aug 2025 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>I couldn't attend the event, so I followed the path on my own today, it was too warm and I had no energy.</p>
<p>You might prefer to join the great adventure 👉 https://trail-maniacs.ch/trail-run-jenaz/</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Swissalpine K43]]></title>
          <link>https://daviddalbusco.com/trails/2025/swissalpine-k43/</link>
          <pubDate>Sat, 19 Jul 2025 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>The Swissalpine K43 is a stunning alpine marathon featuring lovely mountain trails around Flims. Highly recommended.</p>
<p>👉 https://swissalpine.ch/en/trails/k43/</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Seedorf UR - Hinter Jochli - Dallenwil]]></title>
          <link>https://daviddalbusco.com/trails/2025/seedorf-ur-hinter-jochli-dallenwil/</link>
          <pubDate>Sat, 12 Jul 2025 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>Started from Seedorf with a peaceful 2 km along the lake, such a nice way to kick things off. Then up through Isenthal, a first for me: quiet, absolutely beautiful, barely anyone around. Steady climb all the way to Hinter Jochli, where things really open up. From there, all downhill: passing Brisenhaus, through Niederrickenbach, and ending in Dallenwil.</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Alpnachstad - Pilatus]]></title>
          <link>https://daviddalbusco.com/trails/2025/alpnachstad-pilatus/</link>
          <pubDate>Sat, 05 Jul 2025 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>An uncomplicated — maybe a bit too short — trail loop from Alpnachstad (train station) to Pilatus. The ascent is really pretty with nice scenery, and the way down is easy to run and peaceful. The summit, as well as the train ride, is absolutely packed with tourists and therefore not that lovely. Probably better to go there during weekdays.</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Vier-Seen-Wanderung+]]></title>
          <link>https://daviddalbusco.com/trails/2025/vier-seen-wanderung/</link>
          <pubDate>Sun, 22 Jun 2025 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>There were so many people — really, a crowd. Even on the way home, the public transport was packed: the bus was overflowing, and the train was no better.</p>
<p>Long story short:</p>
<p>Is it a nice trail? Yes.<br />
Should you go when the weather is absolutely perfect on a weekend? Probably not.</p>
<p>Waypoints:</p>
<ul>
<li>Engelberg</li>
<li>Jochpass</li>
<li>Melchsee-Frutt</li>
<li>Melchtal</li>
</ul>
<p>Tips:</p>
<p>There's only one bus per hour from Melchtal Dorf (today it was at :48). When you pass Stöckalp (about 3 km before Melchtal), check the time. If you're close to the next bus, I suggest catching it there. The wait in Melchtal isn’t really worth the extra few kilometers — they’re okay, but nothing spectacular.</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Brunnen - Frontalpstock]]></title>
          <link>https://daviddalbusco.com/trails/2025/brunnen-frontalpstock/</link>
          <pubDate>Sun, 15 Jun 2025 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>A fun and very easy trail run — with minimal technical sections — from Brunnen to Fronalpstock, with a few extra meters added just for fun.</p>
<p>Stoos and Fronalpstock can be a bit touristy, so the trail turned into a surprisingly social experience — quite enjoyable overall.</p>
<p>Instead of doing an out-and-back to Fronalpstock, you can extend the route to Klingenstock and make it a loop. It adds 2–3 km and a bit of up and down, but the weather was turning today and thunderstorms were forecast, so keeping it simple was the better choice.</p>
<p>The trail starts and ends at Brunnen train station. You can also shorten it in Stoos or Fronalpstock — both have ski lifts (also operating in summer).</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Zurich - Zug]]></title>
          <link>https://daviddalbusco.com/trails/2025/zurich-zug/</link>
          <pubDate>Sat, 03 May 2025 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>Easy run from Zurich to Zug.</p>
<p>The last few kilometers are on asphalt with heavy car traffic. You can avoid this section and shorten the route by taking a bus from Sihlbrugg.</p>
<p>Roughly two-thirds into the run, there’s a wooden observation tower. Climbing the stairs is worth it for the view. At the top, you'll get a 360° panorama overlooking both Lake Zurich and Lake Zug.</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Kreis 5 - Regensdorf - Katzensee - Käferberg - Kreis 5]]></title>
          <link>https://daviddalbusco.com/trails/2025/kreis-5-regensdorf-katzensee-kaferberg-kreis-5/</link>
          <pubDate>Sat, 19 Apr 2025 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>Easy run. A bit of asphalt.</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[How to Assert a Custom Section in a WASM (in JavaScript)]]></title>
          <description><![CDATA[Read and assert a WebAssembly module metadata using Vitest, a quick recipe.]]></description>
          <link>https://daviddalbusco.com/blog/how-to-assert-a-custom-section-in-a-wasm-in-javascript-test/</link>
          <pubDate>Mon, 14 Apr 2025 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*U-RfGMLu6r_dkcD1FveD_A.jpeg" alt="" / loading="lazy"></p>
<blockquote>
<p>Photo by <a href="https://unsplash.com/fr/@pawel_czerwinski?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash" rel="noopener noreferrer external">Pawel Czerwinski</a> on <a href="https://unsplash.com/fr/photos/motif-darriere-plan-HypiDneHQsQ?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash" rel="noopener noreferrer external">Unsplash</a></p>
</blockquote>
<hr />
<p>Not sure if this blog post will ever be read or useful to anyone — feels like one of those super ultra-niche things. And to be honest, it’s nothing groundbreaking either. If you check MDN or ask your favorite AI, you’ll probably figure it out pretty quickly.</p>
<p>But hey, I felt like sharing anyway.</p>
<p>On <a href="https://juno.build/" rel="noopener noreferrer external">Juno</a>, every container — or smart contract, or idenpendent microservice (call them what you want) — is a WebAssembly module running on the Internet Computer. While I was building support for serverless functions written in TypeScript, I ran into a need, keeping track of the version of each deployed function written by the developers.</p>
<p>After a few iterations, I landed on an approach where each module gets a pseudo-<code>package.json</code> embedded as a custom section in the WASM binary. This way, any part of the ecosystem — whether tooling or the platform itself — can read the module's version and dependencies.</p>
<p>Once the feature was working, I knew I needed a test for it. Embedding and exposing that metadata is pretty critical. So I wrote a Vitest assertion to validate the metadata, and figured I’d share the solution with you here.</p>
<hr />
<h2>The High-Level Idea</h2>
<p>I needed to make sure my WASM modules actually include the custom <code>juno:package</code> section I add during build time — and that the metadata inside it is correct.</p>
<p>So I wrote a simple Vitest test that:</p>
<ol>
<li>Reads the compiled <code>.wasm</code> file</li>
<li>Extracts the custom section named <code>icp:public juno:package</code></li>
<li>Parses it as JSON</li>
<li>Asserts the content matches what I expect</li>
</ol>
<p>Here’s what that looks like:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">it</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">should expose public custom section juno:package</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> junoPkg </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> customSectionJunoPackage</span><span style="color:#F8F8F2">({ path</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> SATELLITE_WASM_PATH });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    expect</span><span style="color:#F8F8F2">(junoPkg).</span><span style="color:#50FA7B">toEqual</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        name</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@junobuild/satellite</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        version</span><span style="color:#FF79C6">:</span><span style="color:#50FA7B"> readWasmVersion</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">satellite</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><hr />
<h2>Reading the Custom Section</h2>
<p>The <code>customSectionJunoPackage()</code> helper is just a tiny wrapper that calls a more generic <code>customSection()</code> function and parses the result as JSON:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> customSectionJunoPackage</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">path</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> section </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> customSection</span><span style="color:#F8F8F2">({ path, sectionName</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">icp:public juno:package</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> });</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9"> JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">parse</span><span style="color:#F8F8F2">(section);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>I wrapped the logic in a helper because — who knows — I might need to read other sections in the future. Also, my section name is prefixed with <code>icp:public</code> because, according to the spec, custom metadata needs that prefix to be exposed publicly on the network.</p>
<p>That said, nothing fancy. The real work happens inside <code>customSection()</code> — that’s where we load the WASM, decompress it (since I gzip it during the build), and extract the actual custom section.</p>
<p>Here’s how that looks:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> customSection</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ({</span></span>
<span class="line"><span style="color:#F8F8F2">    path,</span></span>
<span class="line"><span style="color:#F8F8F2">    sectionName</span></span>
<span class="line"><span style="color:#F8F8F2">}</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    path</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    sectionName</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">})</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#6272A4">    // Read WASM</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> buffer </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> readFile</span><span style="color:#F8F8F2">(path);</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> wasm </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> gunzipFile</span><span style="color:#F8F8F2">({ source</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> buffer });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // Compile a WebAssembly.Module object</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> wasmModule </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> WebAssembly.</span><span style="color:#50FA7B">compile</span><span style="color:#F8F8F2">(wasm);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // Read the public custom section</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> pkgSections </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> WebAssembly.Module.</span><span style="color:#50FA7B">customSections</span><span style="color:#F8F8F2">(wasmModule, sectionName);</span></span>
<span class="line"><span style="color:#50FA7B">    expect</span><span style="color:#F8F8F2">(pkgSections).</span><span style="color:#50FA7B">toHaveLength</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // Parse content to object</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> [pkgBuffer] </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> pkgSections;</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#50FA7B"> uint8ArrayToString</span><span style="color:#F8F8F2">(pkgBuffer);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Now let’s walk through it step by step.</p>
<ol>
<li>Read the <code>.wasm</code> file</li>
</ol>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { readFile } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">fs/promises</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> buffer </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> readFile</span><span style="color:#F8F8F2">(path);</span></span></code></pre><p>Pretty straightforward — this reads the file from disk (either absolute or relative path) and gives us a raw buffer.</p>
<ol start="2">
<li>Decompress it</li>
</ol>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { gunzipFile } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@junobuild/cli-tools</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> wasm </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> gunzipFile</span><span style="color:#F8F8F2">({ source</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> buffer });</span></span></code></pre><p>Since I gzip the <code>.wasm</code> file during the build step (mostly for size reasons), I need to decompress it before I can inspect it. <code>gunzipFile</code> is a small helper I use for this — but under the hood, it just relies on the Node.js <code>zlib</code> API.</p>
<p>Here’s what it looks like:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Readable } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">node:stream</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { createGunzip } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">node:zlib</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> gunzipFile</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">source</span><span style="color:#F8F8F2"> }</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { source</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Buffer</span><span style="color:#F8F8F2"> })</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Buffer</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=></span></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">reject</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> sourceStream </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> Readable.</span><span style="color:#50FA7B">from</span><span style="color:#F8F8F2">(source);</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> chunks</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Uint8Array</span><span style="color:#F8F8F2">[] </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> [];</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> gzip </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> createGunzip</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        sourceStream.</span><span style="color:#50FA7B">pipe</span><span style="color:#F8F8F2">(gzip);</span></span>
<span class="line"><span style="color:#F8F8F2">        gzip.</span><span style="color:#50FA7B">on</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">data</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, (</span><span style="color:#FFB86C;font-style:italic">chunk</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> chunks.</span><span style="color:#50FA7B">push</span><span style="color:#F8F8F2">(chunk));</span></span>
<span class="line"><span style="color:#F8F8F2">        gzip.</span><span style="color:#50FA7B">on</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">end</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, () </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> resolve</span><span style="color:#F8F8F2">(Buffer.</span><span style="color:#50FA7B">concat</span><span style="color:#F8F8F2">(chunks)));</span></span>
<span class="line"><span style="color:#F8F8F2">        gzip.</span><span style="color:#50FA7B">on</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">error</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, reject);</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span></code></pre><ol start="3">
<li>Compile it to a WebAssembly module</li>
</ol>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> wasmModule </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> WebAssembly.</span><span style="color:#50FA7B">compile</span><span style="color:#F8F8F2">(wasm);</span></span></code></pre><p>This step turns the decompressed binary into an actual <code>WebAssembly.Module</code>. This is required because, well, we can’t directly inspect sections from raw bytes — we need a compiled module first (<a href="https://developer.mozilla.org/en-US/docs/WebAssembly/Reference/JavaScript_interface/compile_static" rel="noopener noreferrer external">MDN docs</a>).</p>
<p>⚠️ We’re not <em>instantiating</em> the module (i.e., not running it), we’re just compiling it so we can poke around inside.</p>
<ol start="4">
<li>Read the custom section and make sure it exists</li>
</ol>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> pkgSections </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> WebAssembly.Module.</span><span style="color:#50FA7B">customSections</span><span style="color:#F8F8F2">(wasmModule, sectionName);</span></span>
<span class="line"><span style="color:#50FA7B">expect</span><span style="color:#F8F8F2">(pkgSections).</span><span style="color:#50FA7B">toHaveLength</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">);</span></span></code></pre><p>Here’s where the magic happens.</p>
<p>The <code>WebAssembly.Module.customSections()</code> method lets you extract any custom sections by name — in my case, the <code>icp:public juno:package</code> key.</p>
<p>Since a WASM module can technically have multiple sections, I get them as an array. That’s why I assert its length to make sure the section is actually there — because if it’s not, something went wrong in the build.</p>
<p>No section? No metadata anyway.</p>
<ol start="5">
<li>Convert the section to a string</li>
</ol>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { uint8ArrayToString } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">uint8array-extras</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> [pkgBuffer] </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> pkgSections;</span></span>
<span class="line"><span style="color:#FF79C6">return</span><span style="color:#50FA7B"> uint8ArrayToString</span><span style="color:#F8F8F2">(pkgBuffer);</span></span></code></pre><p>The section we get is a <code>ArrayBuffer</code>, so the last step is just turning it into a string. In my case, that string is JSON, but at this level, we don’t assume anything — just return it as-is.</p>
<p>Later, in <code>customSectionJunoPackage()</code>, I parse it with <code>JSON.parse()</code>, but this low-level helper could work for any kind of string-based metadata.</p>
<p>For conversion, I use a helper from the <a href="https://github.com/sindresorhus/uint8array-extras" rel="noopener noreferrer external">uint8array-extras</a> library as I already depend on it in the project since I’m using other CLI tools from the same author.</p>
<p>That’s it. No WebAssembly voodoo here. Just basic file I/O, a bit of decompression, and some native JS/WebAssembly APIs doing their thing.</p>
<hr />
<h2>Bonus: Reading the Original Cargo.toml</h2>
<p>You might’ve noticed the <code>readWasmVersion()</code> function back in the test. That’s there to make sure the version embedded in the WASM’s <code>juno:package</code> section is actually the one defined in the original <code>Cargo.toml</code>.</p>
<p>Since my build pipeline grabs the version from that file, I want my test to confirm it’s doing the right thing.</p>
<p>Here’s how I read the version from the TOML:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { parse } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@ltd/j-toml</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { assertNonNullish } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@dfinity/utils</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> readWasmVersion</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">segment</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> tomlFile </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> readFileSync</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">join</span><span style="color:#F8F8F2">(process.</span><span style="color:#50FA7B">cwd</span><span style="color:#F8F8F2">(), </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">src</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, segment, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Cargo.toml</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">));</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    type</span><span style="color:#8BE9FD;font-style:italic"> Toml</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> { package</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { version</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2"> } } </span><span style="color:#FF79C6">|</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> result</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Toml</span><span style="color:#FF79C6"> =</span><span style="color:#50FA7B"> parse</span><span style="color:#F8F8F2">(tomlFile.</span><span style="color:#50FA7B">toString</span><span style="color:#F8F8F2">()) </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> unknown</span><span style="color:#FF79C6"> as</span><span style="color:#8BE9FD;font-style:italic"> Toml</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> version </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> result?.package?.version;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    assertNonNullish</span><span style="color:#F8F8F2">(version);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> version;</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>I use the <a href="https://github.com/LongTengDao/j-toml" rel="noopener noreferrer external">j-toml</a> library to parse TOML in a way that’s faithful enough to trust when bundling and reading and <a href="https://github.com/dfinity/ic-js/tree/main/packages/utils#gear-assertnonnullish" rel="noopener noreferrer external">assertNonNullish()</a> is just a handy helper I use across the codebase — it throws if the value is <code>null</code> or <code>undefined</code>.</p>
<p>This way, the test isn’t just checking <em>if</em> the metadata is present — it’s making sure it’s <em>correct</em>.</p>
<hr />
<h2>That’s a Wrap</h2>
<p>That’s pretty much it. A small test, a bit of metadata, and some light WASM poking. Again, not sure if this will ever be helpful for anyone — or if anyone will even read it — but if you did, hope it was enjoyable.</p>
<p>Cheers,<br />
David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Kreis 5 - Adliswil - Uetliberg - Kreis 5]]></title>
          <link>https://daviddalbusco.com/trails/2025/kreis-5-adliswil-uetliberg-kreis-5/</link>
          <pubDate>Sat, 05 Apr 2025 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>Preseason classic.</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Bye Bye, Firebase Hosting $$$]]></title>
          <description><![CDATA[I’ve been using Firebase Hosting for the last six years, but the new pricing changes have pushed me away.]]></description>
          <link>https://daviddalbusco.com/blog/bye-bye-firebase-hosting/</link>
          <pubDate>Tue, 25 Feb 2025 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*rkJkG5mkhIDjcJrCR1fivQ.jpeg" alt="" / loading="lazy"></p>
<blockquote>
<p>Photo by <a href="https://unsplash.com/fr/@simplicity?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash" rel="noopener noreferrer external">Marija Zaric</a> on <a href="https://unsplash.com/fr/photos/blanc-et-noir-je-taime-impression-sur-mur-en-beton-gris-q73jLftKN-A?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash" rel="noopener noreferrer external">Unsplash</a></p>
</blockquote>
<hr />
<p>I’ve been using Firebase to host my website — <a href="https://daviddalbusco.com/" rel="noopener noreferrer external">daviddalbusco.com</a> — for the last six years. It was convenient and “free” (quotes intentional because, one way or another, Google makes money).</p>
<p>But recently, they changed their pricing model, and well... let’s just say it’s not great. This month alone, I’m set to pay $30 for hosting a simple static site. That feels excessive. I mean, sure, my site serves 2.6GB of data a day, which is over their 360MB free limit, but still — $30? That’s 2–3 times the price of Netflix or Spotify 🤷‍♂️.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*tvNeqm3X1W0YHGt8tOX3tA.png" alt="" / loading="lazy"></p>
<p>And for context, based on the data I have, my traffic hasn't changed - January and February had the same numbers. Some might argue I should focus on blocking bots that generate excessive traffic, and fair enough, I've started looking into it. But the reality is, I never had to worry about it - until Firebase changed its pricing.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*pWv3bYVG7HYNrzG2nt-YEQ.png" alt="" / loading="lazy"></p>
<p>So yeah, I’m moving to <a href="https://juno.build/" rel="noopener noreferrer external"><strong>Juno</strong></a>. 🚀</p>
<hr />
<h2>&quot;Wait… What? You hadn't already migrated to Juno?&quot;</h2>
<p>If you’ve been following my work, you might be wondering:</p>
<blockquote>
<p>“Dude... Juno is literally your project. Why were you still on Firebase?”</p>
</blockquote>
<p>And here’s my confession: <strong>I knew</strong>. I was fully aware my site was still on Firebase. In fact, moving it to Juno was always this unspoken goal I had — kind of like putting the cherry on top. I told myself I’d make the switch the day Juno became a DAO. A way to mark that milestone and personal achievement.</p>
<p>But hey, Firebase “forced” my hand, so here we are.</p>
<hr />
<h2>What About Costs?</h2>
<p>So how does Juno compare?</p>
<p>On the <strong><a href="https://internetcomputer.org/" rel="noopener noreferrer external">Internet Computer</a></strong> — the blockchain that super powers Juno - costs come from storing data and updating state, but querying data — like accessing a website — is currently free. That means as long as this remains the case, hosting a static site on Juno is <strong>cheap</strong>.</p>
<p>I haven’t done the exact math, but I expect it to cost me <strong>around $10 for an entire year</strong>. Yep, <strong>a whole year</strong> for what Firebase was about to charge me in <strong>10 days</strong>.</p>
<p>And even if it ends up being $20 - it's still less than what I paid this month.</p>
<p>Now, obviously, if your site is dynamic and frequently updates state, costs will be different. But for a static site like mine? <strong>Juno now wins, hands down.</strong></p>
<hr />
<h2>Moving to Juno: A Quick Guide</h2>
<p>If you’re in the same boat and want to migrate to Juno, here’s what you need to do:</p>
<h4>1. Sign in to Juno</h4>
<p>Go to <a href="https://console.juno.build" rel="noopener noreferrer external">console.juno.build</a> and sign in. If you don’t have an <strong>Internet Identity (II)</strong> yet, create one. It’s a decentralized, open-source, privacy-friendly authentication method — meaning <strong>you own your data</strong>.</p>
<p>Oh, and by the way — <strong>starting on Juno is free</strong>, so be my guest! 🎉</p>
<h4>2. Create a Satellite</h4>
<p>A Satellite is like a container for your project. Not exactly like a Docker container — it’s a <strong>smart contract</strong> — but you can think of it as something similar, running on a blockchain. Because yes, everything on Juno is <strong>on-chain</strong>. Magic! ✨</p>
<h4>3. Set Up GitHub Actions</h4>
<p>You’ll need to:</p>
<ul>
<li>Generate and add a secret to your repo.</li>
<li>Add a config file to your project.</li>
<li>Set up a GitHub Action to handle deployment.</li>
</ul>
<p>Everything is well-documented (<a href="https://juno.build/docs/guides/github-actions" rel="noopener noreferrer external">here</a>), so just <strong>follow the step-by-step guide</strong> and copy-paste what you need.</p>
<h4>4. Deploy On-Chain</h4>
<p>Once the action runs, <strong>boom — your site is deployed on-chain</strong>. No extra steps. How wonderful.</p>
<h4>5. Update Your DNS</h4>
<p>Now, time to connect <strong>your domain name</strong>:</p>
<ul>
<li>Remove any existing DNS entries you had for Firebase.</li>
<li>Head over to the <strong>Hosting tab</strong> in the <a href="https://console.juno.build/" rel="noopener noreferrer external">Juno Console</a> and register your domain.</li>
<li>The UI will give you the new DNS records to add, and the docs have extra guidance if needed.</li>
</ul>
<h4>6. Delete Your Firebase Project</h4>
<p>Finally, clean up:</p>
<ul>
<li>Delete your Firebase project so you’re not charged anymore.</li>
<li><strong>Remove your credit card from Firebase</strong> if you don’t need it there.</li>
</ul>
<p>And that’s it — you’re free! 🚀</p>
<hr />
<h2>Need Help? Ping Me!</h2>
<p>If you run into any issues migrating to Juno, <strong>let me know</strong>. Always happy to help.</p>
<p>To infinity and beyond,<br />
David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[A New Chapter Awakens]]></title>
          <description><![CDATA[I’ll be working full-time on Juno through 2025 thanks to support from the DFINITY foundation.]]></description>
          <link>https://daviddalbusco.com/blog/a-new-chapter-awakens/</link>
          <pubDate>Fri, 01 Nov 2024 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://images.unsplash.com/photo-1541185934-01b600ea069c?fm=jpg&amp;q=80&amp;w=1080&amp;ixlib=rb-4.0.3&amp;ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" alt="Working on Sunday" / loading="lazy"></p>
<blockquote>
<p>Photo by <a href="https://unsplash.com/fr/@spacex?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash" rel="noopener noreferrer external">SpaceX</a> on <a href="https://unsplash.com/fr/photos/horizon-au-crepuscule-6SbFGnQTE8s?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash" rel="noopener noreferrer external">Unsplash</a></p>
</blockquote>
<hr />
<blockquote>
<p><strong>TL;DR</strong></p>
<p>I’ll be working 100% on Juno through 2025.</p>
</blockquote>
<p><br /></p>
<p>Hey everyone 👋</p>
<p>As you may know, I recently proposed transforming Juno into a Decentralized Autonomous Organization through an SNS swap. Unfortunately, it didn’t reach its funding goal, so Juno didn’t become a DAO.</p>
<p>After the failure, three options came to mind: retrying the ICO with a lower target, continuing to hack as an indie project for a while, or simply killing it.</p>
<p>In the days that followed, I also received a few other options, including interest from venture capitalists for potential seed funding which wasn’t an option for me.</p>
<p>Then, something unexpected happened:</p>
<p>The <a href="https://dfinity.org/" rel="noopener noreferrer external">DFINITY</a> foundation’s CTO, <a href="https://x.com/JanCamenisch" rel="noopener noreferrer external">Jan Camenisch</a>, reached out and proposed an alternative: funding the project through 2025.</p>
<p>I took a few days to consider the offer and ultimately accepted.</p>
<p>This support is a tremendous vote of confidence in Juno’s potential and importance within the ecosystem.</p>
<p>It’s worth emphasizing that the foundation’s support comes with no strings attached. They do not receive any stake in Juno, have no preferential treatment, and will not influence decisions. Should I ever consider another SNS DAO or any other funding route in the future, the foundation would have no special allocation or shares. This remains <em>my project</em>, and I am the sole decision-maker and controller.</p>
<p>This support also strengthens the relationship between Juno and the foundation, allowing us to stay in close contact to discuss the roadmap. It’s an arrangement that respects autonomy while fostering collaboration to advance the <a href="https://internetcomputer.org/" rel="noopener noreferrer external">Internet Computer</a>. As they say, it takes two to tango.</p>
<p>This funding opens up a world of possibilities and marks the first time I’ll work 100% on a project I created. I’m thrilled to continue building Juno as a resource that makes decentralized development accessible and impactful for everyone.</p>
<p>Obviously, while Juno remains under my sole ownership for now, I still believe that Juno should eventually become a DAO. Promoting full control for developers while retaining centralized ownership would be paradoxical. When the time is right, a DAO will ensure that Juno’s growth, security, and transparency are upheld through community-driven governance.</p>
<p>Thank you to everyone who believed in Juno through the SNS campaign and beyond 🙏💙. Your support has been invaluable, and this new phase wouldn’t be possible without you. Here’s to what lies ahead—a new chapter indeed.</p>
<p>To infinity and beyond,<br />
<br/>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Flüelen - Muotathal]]></title>
          <link>https://daviddalbusco.com/trails/2024/fluelen-muotathal/</link>
          <pubDate>Sat, 31 Aug 2024 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>Easy run from Flüelen to Muotathal through Chinzigpass. I'm still not exactly sure what the waypoints were, as I checked the Swisstopo map on my phone far more often than I should admit, but overall, I had a really lovely day. There weren’t that many people, and there were enough alpine restaurants to get a refill or drink something. Today was sunny and 28°C, therefore it was really welcome.</p>
<p>Worth noting that after a few kilometers, the route actually joins the first stage of the Tell Trail: https://www.schwyz-tourismus.ch/de/detail/poitype/outdooractivetour/poi/tell-trail-etappe-1-altdorf-muotatal-1/</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Pizza Grossa Loop]]></title>
          <link>https://daviddalbusco.com/trails/2024/pizza-grossa-loop/</link>
          <pubDate>Sun, 11 Aug 2024 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>The trail above Savogning was truly lovely, offering breathtaking views and a sense of wildness with very few people along the way. However, there's a catch: there are no catering facilities on the route!</p>
<p>I brought 1.5 liters of water, but I still ended up dehydrated during the last few kilometers due to the heat wave, which explains why it took me so long to complete the trail.</p>
<p>So, be cautious if you decide to tackle it, but aside from that, it's an amazing and relatively easy run!</p>
<hr />
<p>The trail starts and ends in Savognin.</p>
<p>Waypoints (really easy to follow):</p>
<ul>
<li>Lai Tigiel</li>
<li>Motta d'Err</li>
<li>Via Penza</li>
</ul>
<hr />
<p>Links: https://www.graubuenden.ch/en/tours/trailrun-pizza-grossa-loop</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Altdorf - Surenenpass- Altdorf]]></title>
          <link>https://daviddalbusco.com/trails/2024/altdorf-surenenpass-altdorf/</link>
          <pubDate>Sun, 16 Jun 2024 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>So much snow!</p>
<p>My original idea was to run the Via Alpina Stage 7 from Altdorf (UR) to Engelberg, but it felt too risky to continue the route and pass the Surenenpass.</p>
<p>It was still an amazing journey, with stunning views and very few other hikers around.</p>
<p>However, if you plan to follow this route, I advise you to check out the official website for the trail:</p>
<ul>
<li><p>https://schweizmobil.ch/en/hiking-in-switzerland/route-1/stage-7</p></li>
<li><p>https://www.engelberg.ch/sommer/wandern/wanderrouten/tour/via-alpina-c7-altdorf-engelberg/24501890/?cHash=f1ce0913262a11eb992b640c6d6c4e4f</p></li>
</ul>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Jura Crest Trail: Stage 2+3]]></title>
          <link>https://daviddalbusco.com/trails/2024/jura-crest-trail-stage-23/</link>
          <pubDate>Sun, 12 May 2024 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>The trail begins in Brugg and winds through fields and forests up to Hauenstein. It's a pleasant run that feels predominantly uphill, with very few people around. There are limited options for refilling water, so it's important to monitor your hydration.</p>
<p>Waypoints:</p>
<p>Brugg<br />
Staffelegg<br />
Hauenstein</p>
<p>Follow the yellow sign &quot;5 Jura-Höhenweg&quot;.</p>
<p>Links:</p>
<ul>
<li>https://schweizmobil.ch/fr/suisse-a-pied/itineraire-5/etape-2</li>
<li>https://schweizmobil.ch/fr/suisse-a-pied/itineraire-5/etape-3</li>
</ul>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Jura Crest Trail: Stage 1]]></title>
          <link>https://daviddalbusco.com/trails/2024/jura-crest-trail-stage-1/</link>
          <pubDate>Thu, 18 Apr 2024 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>The trail starts in Dielsdorf and begins with a gentle climb through the vineyards to Regensberg—a lovely town built on a rocky spur. It continues through the forest until you reach the Jura hill.</p>
<p>Running the summit of the hills is a bit technical, and the path is partly exposed but really enjoyable with a view of the Alps.</p>
<p>From there, you'll descend to reach the old town of Baden by crossing the bridge near the Bailiff's Castle. I lost my way in the city; therefore, the GPS track isn't that straightforward at that location.</p>
<p>After going up to the castle at the top of the city, the run continues and finishes in Brugg, mostly through forest roads.</p>
<p>In short: it starts in Dielsdorf and follows the yellow sign &quot;5 Jura-Höhenweg&quot;.</p>
<p>Waypoints:</p>
<ul>
<li>Dielsdorf</li>
<li>Regensberg</li>
<li>Baden</li>
<li>Brugg</li>
</ul>
<p>Links:</p>
<ul>
<li>https://schweizmobil.ch/en/hiking-in-switzerland/route-5/stage-1</li>
</ul>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Make AI-generated image (kind of) from drawing]]></title>
          <description><![CDATA[How to create SVG images with ChatGPT from hand-drawn sketches.]]></description>
          <link>https://daviddalbusco.com/blog/make-ai-generated-image-kind-of-from-drawing/</link>
          <pubDate>Mon, 01 Apr 2024 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*MtSx1X7gJiyWtTSrxL6Tdg.png" alt="" / loading="lazy"></p>
<hr />
<p>I’ve never been a fan of slides. Similarly, I find the typical “hello world” demos a bit too simplistic for my taste. So, when I was recently given the opportunity to demonstrate the joys of local development with <a href="https://juno.build/" rel="noopener noreferrer external">Juno</a> — essentially, to highlight the cool aspects of the developer experience — I naturally gravitated towards live coding. I decided to dive deep and spend a few days crafting an <a href="https://pycrs-xiaaa-aaaal-ab6la-cai.icp0.io/" rel="noopener noreferrer external">app</a> that’s nearly fully featured.</p>
<p>This project also marked my first foray into using the ChatGPT API. It seemed like the perfect chance to not only broaden my skill set but to explore an intriguing possibility: generating an image from another image using AI. As of March 2024, this functionality isn’t directly offered by OpenAI. However, I’ve discovered a creative workaround, which I’m excited to share with you in this blog post.</p>
<blockquote>
<p>Side note: For those of you who are curious about the nuts and bolts of my demo, or just love diving into the details, I’ve shared a thread on <a href="https://twitter.com/daviddalbusco/status/1765443683606839566" rel="noopener noreferrer external">X / Twitter </a>complete with videos and a link to the repository.</p>
</blockquote>
<hr />
<h2>The Concept</h2>
<p>This approach is built upon a simple yet inventive use of technology. Users start by creating a drawing, which is then uploaded to the web. From there, it’s propelled into the next phase of its transformation through the capabilities of OpenAI’s Vision API.</p>
<p>The Vision API is primarily designed to understand and interpret images — to identify what’s depicted in a picture and answer questions about its contents. It’s not inherently built to create new images from existing ones. However, it has the ability to generate text-based responses, including code, based on the visual information it processes.</p>
<p>Capitalizing on this functionality, the idea emerged to leverage the API’s capacity for generating code, specifically in the form of SVG. SVG is a versatile image format defined in XML code, which means that, indirectly, the Vision API can be prompted to “generate” an image by producing SVG code.</p>
<p>This method doesn’t generate an image in the traditional sense, where an AI directly creates a new visual piece. Instead, it transforms the interpretation of the original drawing into a new format — an SVG — based on the AI’s understanding and descriptive capabilities. It’s a creative workaround that combines the analytical strength of AI with the imaginative expressions of users, resulting in a unique digital alchemy that turns drawings into a form of AI-generated art, albeit through an unconventional path.</p>
<hr />
<h2>Getting Started</h2>
<p>Having laid the groundwork with our concept, let’s move forward by crafting a minimalist application to implement it.</p>
<p>First, we’ll start with a basic HTML page. It will feature a button to initiate the SVG generation process and a div element where the AI-generated SVG will be rendered.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;!</span><span style="color:#FF79C6">doctype</span><span style="color:#50FA7B;font-style:italic"> html</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">html</span><span style="color:#50FA7B;font-style:italic"> lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">en</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">body</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">button</span><span style="color:#50FA7B;font-style:italic"> id</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">generate</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> type</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">button</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Generate&#x3C;/</span><span style="color:#FF79C6">button</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> id</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">output</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#50FA7B;font-style:italic"> type</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">module</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/src/main.ts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">body</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">html</span><span style="color:#F8F8F2">></span></span></code></pre><p>Next, we’ll implement a script that handles the interaction by binding a click event to the button and rendering the AI-generated SVG in the DOM.</p>
<blockquote>
<p>Note that I’m skipping nullish validation and error handling for the sake of simplifying this post.</p>
</blockquote>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { generate } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./chatgpt.ts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">document.</span><span style="color:#50FA7B">addEventListener</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">DOMContentLoaded</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> btn </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> document.</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">#generate</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#50FA7B"> onClick</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">            const</span><span style="color:#F8F8F2"> { svgOuterHTML } </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> generate</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">            document.</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">#output</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">).innerHTML </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> svgOuterHTML;</span></span>
<span class="line"><span style="color:#F8F8F2">        };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        btn.</span><span style="color:#50FA7B">addEventListener</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">click</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, onClick, { passive</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2"> });</span></span>
<span class="line"><span style="color:#F8F8F2">    },</span></span>
<span class="line"><span style="color:#F8F8F2">    { once</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2"> }</span></span>
<span class="line"><span style="color:#F8F8F2">);</span></span></code></pre><p>At this stage, we’re ready to set up the generate function. This function will kick off the process with the OpenAI API and handle the result, ultimately providing it back to the user. For now, we'll outline a basic version and refine it with the actual API call later:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> generate</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;{ svgOuterHTML</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2"> }> </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> previewResponse </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> call</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> { svgOuterHTML</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">TODO implement me</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> };</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><hr />
<h2>Integrating OpenAI API</h2>
<p>In this chapter, we dive deeper into the technical aspects of making the call to the OpenAI API, crucial for transforming user sketches into high-fidelity SVG images. Here’s how we go about it:</p>
<ol>
<li><p><strong>Securing an OpenAI API Key</strong>: The process begins with obtaining an OpenAI API key, a crucial step for authentication. In our example, we represent this key with a constant called OPENAI_API_KEY. This key should be replaced by your own key.</p></li>
<li><p><strong>Crafting the System Prompt</strong>: The heart of our request to OpenAI lies in a system prompt. This detailed prompt guides the AI to understand exactly what we’re asking for — a high-fidelity SVG based on a low-fidelity sketch. It not only sets the context for the AI, outlining a specific expertise in web design and SVG programming inspired by expressionism, surrealism, and Bauhaus design principles — you can, of course, be more creative with those details when implementing your own solution — but it also explicitly instructs the AI to return a single SVG file. This ensures the AI’s output aligns precisely with our needs.</p></li>
<li><p><strong>Preparing the Fetch Request</strong>: The body of our fetch call is meticulously prepared to communicate our requirements to OpenAI. It includes the system prompt and a URL to the source image, here represented by DRAWING_URL. This URL points to a sketch that we've uploaded to the web. Though the process of converting a user's drawing to an image and uploading it isn't covered here, it's a necessary step before making this call.</p></li>
<li><p><strong>Executing the Call and Handling the Response</strong>: With everything in place, we execute a fetch request to OpenAI's API. This request is sent with the appropriate headers, including our API key for authentication. Upon receiving a response, we check for success and then parse the JSON data. This data, ideally, contains the SVG code generated by the AI in response to our prompt and image.</p></li>
</ol>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> OPENAI_API_KEY </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">my_openai_api_key</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> SYSTEM_PROMPT </span><span style="color:#FF79C6">=</span><span style="color:#F1FA8C"> `You are an expert web designer who specializes in programming SVG.</span></span>
<span class="line"><span style="color:#F1FA8C">You are famous for designing those SVG while following movements of arts that included expressionism and surrealism.</span></span>
<span class="line"><span style="color:#F1FA8C">You explored color theory as well.</span></span>
<span class="line"><span style="color:#F1FA8C">You used to taught at the Bauhaus school of art and design in Germany.</span></span>
<span class="line"><span style="color:#F1FA8C">A user will provide you with a low-fidelity sketch of an drawing.</span></span>
<span class="line"><span style="color:#F1FA8C">You will return a single SVG file to create a high fidelity improved drawing that can be used as a background or hero pane on a website.</span></span>
<span class="line"><span style="color:#F1FA8C">You like to add colors and gradients in your work so will you do with this SVG.</span></span>
<span class="line"><span style="color:#F1FA8C">You also like to add some forms and additional objects of your own creation within the drawing.</span></span>
<span class="line"><span style="color:#F1FA8C">You are also a big fan a punk music which influences your style by making things not too square.</span></span>
<span class="line"><span style="color:#F1FA8C">The user will provide you with notes in blue or red text, arrows, or drawings.</span></span>
<span class="line"><span style="color:#F1FA8C">Respond ONLY with the contents of the SVG file.`</span><span style="color:#F8F8F2">; </span><span style="color:#6272A4">// &#x3C;--- We only want SVG</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> DRAWING_URL </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">https://domain.com/your-drawing.png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> call</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Gpt4VisionPreviewResponse</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> body</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> GPT4VVisionPayload</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        model</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">gpt-4-vision-preview</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        max_tokens</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 4096</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        temperature</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        messages</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#F8F8F2">            {</span></span>
<span class="line"><span style="color:#F8F8F2">                role</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">system</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">                content</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> SYSTEM_PROMPT</span></span>
<span class="line"><span style="color:#F8F8F2">            },</span></span>
<span class="line"><span style="color:#F8F8F2">            {</span></span>
<span class="line"><span style="color:#F8F8F2">                role</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">user</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">                content</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#F8F8F2">                    {</span></span>
<span class="line"><span style="color:#F8F8F2">                        type</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">image_url</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">                        image_url</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">                            url</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> DRAWING_URL,</span></span>
<span class="line"><span style="color:#F8F8F2">                            detail</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">high</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">                        }</span></span>
<span class="line"><span style="color:#F8F8F2">                    },</span></span>
<span class="line"><span style="color:#F8F8F2">                    {</span></span>
<span class="line"><span style="color:#F8F8F2">                        type</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">text</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">                        text</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Turn this into a single svg file.</span><span style="color:#E9F284">"</span><span style="color:#6272A4"> // &#x3C;--- We only want SVG</span></span>
<span class="line"><span style="color:#F8F8F2">                    }</span></span>
<span class="line"><span style="color:#F8F8F2">                ]</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"><span style="color:#F8F8F2">        ]</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> response </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> fetch</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://api.openai.com/v1/chat/completions</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, {</span></span>
<span class="line"><span style="color:#F8F8F2">        method</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">POST</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        headers</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#E9F284">            "</span><span style="color:#F1FA8C">Content-Type</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">application/json</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">            Authorization</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> `Bearer </span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">OPENAI_API_KEY</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span></span>
<span class="line"><span style="color:#F8F8F2">        },</span></span>
<span class="line"><span style="color:#F8F8F2">        body</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">stringify</span><span style="color:#F8F8F2">(body)</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">response.ok) {</span></span>
<span class="line"><span style="color:#FF79C6">        throw</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Error</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`Response not ok. Status </span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">response.status</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">. Message </span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">response.statusText</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">.`</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> response.</span><span style="color:#50FA7B">json</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Note that for this solution, I used my own TypeScript types, as detailed below. Alternatively, you can use the official Node.js/TypeScript library for the OpenAI API (<a href="https://github.com/openai/openai-node" rel="noopener noreferrer external">repo</a>).</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">/**</span></span>
<span class="line"><span style="color:#6272A4"> * Request</span></span>
<span class="line"><span style="color:#6272A4"> */</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">type</span><span style="color:#8BE9FD;font-style:italic"> GPT4VVisionPayloadMessageContent</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#FF79C6">    |</span><span style="color:#8BE9FD;font-style:italic"> string</span></span>
<span class="line"><span style="color:#FF79C6">    |</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#FF79C6">            |</span><span style="color:#8BE9FD;font-style:italic"> string</span></span>
<span class="line"><span style="color:#FF79C6">            |</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">                    type</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">image_url</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">                    image_url</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">                        url</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">                        detail</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">low</span><span style="color:#E9F284">"</span><span style="color:#FF79C6"> |</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">high</span><span style="color:#E9F284">"</span><span style="color:#FF79C6"> |</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">auto</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">                    };</span></span>
<span class="line"><span style="color:#F8F8F2">              }</span></span>
<span class="line"><span style="color:#FF79C6">            |</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">                    type</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">text</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">                    text</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">              }</span></span>
<span class="line"><span style="color:#F8F8F2">      )[];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> interface</span><span style="color:#8BE9FD;font-style:italic"> GPT4VVisionPayloadMessage</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    role</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">system</span><span style="color:#E9F284">"</span><span style="color:#FF79C6"> |</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">user</span><span style="color:#E9F284">"</span><span style="color:#FF79C6"> |</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">assistant</span><span style="color:#E9F284">"</span><span style="color:#FF79C6"> |</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">function</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    content</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> GPT4VVisionPayloadMessageContent</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    name</span><span style="color:#FF79C6">?:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> interface</span><span style="color:#8BE9FD;font-style:italic"> GPT4VVisionPayload</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    model</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">gpt-4-vision-preview</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    max_tokens</span><span style="color:#FF79C6">?:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    temperature</span><span style="color:#FF79C6">?:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    messages</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> GPT4VVisionPayloadMessage</span><span style="color:#F8F8F2">[];</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">/**</span></span>
<span class="line"><span style="color:#6272A4"> * Response</span></span>
<span class="line"><span style="color:#6272A4"> */</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">interface</span><span style="color:#8BE9FD;font-style:italic"> Gpt4VisionPreviewResponse</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    id</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    object</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    created</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    model</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    usage</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Gpt4VisionPreviewUsage</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    choices</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Gpt4VisionPreviewChoice</span><span style="color:#F8F8F2">[];</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">interface</span><span style="color:#8BE9FD;font-style:italic"> Gpt4VisionPreviewUsage</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    prompt_tokens</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    completion_tokens</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    total_tokens</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">interface</span><span style="color:#8BE9FD;font-style:italic"> Gpt4VisionPreviewChoice</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    message</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Gpt4VisionPreviewMessage</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    finish_reason</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> String</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    index</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">interface</span><span style="color:#8BE9FD;font-style:italic"> Gpt4VisionPreviewMessage</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    role</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    content</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h2>Finalizing: Processing AI Responses</h2>
<p>In the concluding chapter, we circle back to complete our generate function, initially outlined but left unfinished in a previous section.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> generate</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;{ svgOuterHTML</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2"> }> </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> previewResponse </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> call</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        choices</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [choice]</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> previewResponse;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        message</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { content }</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> choice;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // Stripping the markdown code block syntax to isolate the SVG code</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> { svgOuterHTML</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> content.</span><span style="color:#50FA7B">replace</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">```svg</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">""</span><span style="color:#F8F8F2">).</span><span style="color:#50FA7B">replace</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">```</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">""</span><span style="color:#F8F8F2">) };</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>This function invokes the call function, which communicates with the OpenAI API to submit our request. Upon receiving the response, it extracts the first choice (assuming the API might return multiple options) and then focuses on the message content, which contains our SVG code wrapped in markdown syntax. The final step strips this syntax to yield clean SVG code, ready for rendering in our application’s UI.</p>
<hr />
<h2>Wrapping Up</h2>
<p>And there you have it — the entirety of our solution, from concept to completion. If you’ve followed along and implemented this on your side, you should see results similar to the one in the screenshot I’m sharing below, which demonstrates the transformation of a hand-drawn pineapple into a refined SVG.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*GcCuymVorJx5AuxUlNLtPQ.png" alt="" / loading="lazy"></p>
<p>Thank you for reading! I hope it inspires you to explore further intersections of art, technology, and AI in your projects.</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[TIL Programming Ep1]]></title>
          <description><![CDATA[In this episode, I learned about TypeScript string patterns and Node.js WASM metadata reading.]]></description>
          <link>https://daviddalbusco.com/blog/til-programming-ep1/</link>
          <pubDate>Sun, 25 Feb 2024 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*7DPrmiwTpheVxgpZ9rjSUA.jpeg" alt="" / loading="lazy"></p>
<p>Photo by <a href="https://unsplash.com/fr/@joannakosinska?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash" rel="noopener noreferrer external">Joanna Kosinska</a> on <a href="https://unsplash.com/fr/photos/deux-crayons-gris-sur-surface-jaune-1_CMoFsPfso?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash" rel="noopener noreferrer external">Unsplash</a></p>
<hr />
<p>I’ve been on this blogging journey for a while, aiming to share a least a new post every month. Yet, finding the time and motivation isn’t easy anymore, especially with the seismic shifts in how we consume content post the AI merge. The appetite for long-form blog posts and detailed tutorials seems to be waning, at least from my perspective. Ironically, while my motivation for these extensive pieces dwindles, my activity on <strong><a href="https://twitter.com/daviddalbusco" rel="noopener noreferrer external">X / Twitter</a></strong> remains unabated. There, in the flurry of daily interactions, I often share snippets of what I’ve learned, encapsulated in “TIL…” tweets.</p>
<p>It dawned on me — Why not transform these brief insights into a series of blog posts? And so, here we are, at the inception of what I hope to be a series that blends the brevity of tweets with the depth of a blog. Welcome to the first episode of this journey. Let’s dive in what tips I learned in February 2024!</p>
<hr />
<h2>TIL: Defining String Patterns with TypeScript</h2>
<p>This month, I learned about using template literals in TypeScript to define string patterns.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">type</span><span style="color:#8BE9FD;font-style:italic"> MetamaskChainId</span><span style="color:#FF79C6"> =</span><span style="color:#F1FA8C"> `0x</span><span style="color:#FF79C6">${</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// ❌ Error: Type "123" is not assignable to type '0x${string}'</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> id</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> MetamaskChainId</span><span style="color:#FF79C6"> =</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">123</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// ✅</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> id</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> MetamaskChainId</span><span style="color:#FF79C6"> =</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">0x123</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span></code></pre><p>Introduced in TypeScript 4.1 (I know, it’s been a while, but sometimes I also discover features that have been around for some time 😉), template literal types allow us to create more specific type definitions.</p>
<p>In the snippet above, <code>MetamaskChainId</code> is a type that expects a string starting with &quot;0x&quot;.</p>
<p>While TypeScript will check this at compile time, it's important to remember that these checks don't apply at runtime. This feature isn't a bulletproof solution for type safety but is incredibly useful for making the code more descriptive and guiding its use correctly, especially since I'm using an IDE with IntelliSense.</p>
<hr />
<h2>TIL: Accessing WASM Metadata in Node.js</h2>
<p>This month, I stumbled upon something quite useful: how to read a canister’s WebAssembly (WASM) module’s metadata section using Node.js.</p>
<p>This discovery was not just about the cool factor; it also had practical applications in the new Serverless Function feature of <strong><a href="https://juno.build/" rel="noopener noreferrer external">Juno</a></strong>, where understanding the extensions made to smart contracts by developers can be crucial for the platform.</p>
<p>To access this metadata, the process is straightforward:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> { readFileSync } </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> require</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">node:fs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> wasmBuffer </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> readFileSync</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/path/to/satellite.wasm</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> mod </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#F8F8F2"> WebAssembly.</span><span style="color:#50FA7B">Module</span><span style="color:#F8F8F2">(wasmBuffer);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> metadata </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> WebAssembly.Module.</span><span style="color:#50FA7B">customSections</span><span style="color:#F8F8F2">(mod, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">icp:public juno:build</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> decoder </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> TextDecoder</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> buildType </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> decoder.</span><span style="color:#50FA7B">decode</span><span style="color:#F8F8F2">(metadata[</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">]);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(buildType);</span></span></code></pre><p>To access the metadata of our WebAssembly (WASM), we examine its <code>customSections</code>.</p>
<p>A WASM module comprises various sections, the majority of which adhere to the specifications outlined in the WASM specification. However, developers have the flexibility to include custom sections that are disregarded during the standard validation process.</p>
<p>These custom sections serve as a means to embed supplementary data within WASM modules. For instance, the name custom section enables developers to assign names to all the functions and locals within the module, akin to “symbols” in a native build.</p>
<p>In this context, we instantiate a <code>WebAssembly.Module</code> using a locally loaded WASM file. Subsequently, we extract the <code>customSections</code> and focus on the first one, given the absence of additional metadata in this example.</p>
<hr />
<p>This marks the debut of a blog series blending the brevity of “TIL” tweets. Hope it was a nice short read (for those who are still reading blog posts 😉).</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[My Docker Cheat Sheet: A Newbie’s Toolkit]]></title>
          <description><![CDATA[Simple Docker Commands for Everyday Development on a MacBook M2.]]></description>
          <link>https://daviddalbusco.com/blog/my-docker-cheat-sheet-a-newbies-toolkit/</link>
          <pubDate>Sun, 28 Jan 2024 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*Ryeo2D3AbYwp-vOhW5aDWQ.png" alt="" / loading="lazy"></p>
<hr />
<p>Hey everyone! I’m pretty new to the whole Docker scene and I’ve been hacking around with it to develop a container for <a href="https://juno.build" rel="noopener noreferrer external">Juno</a> devs. I thought I’d share some commands that I’ve found helpful along the way. They’re easy to follow and have been a great aid to me, a self-proclaimed newbie in this domain.</p>
<hr />
<h2>Dockerfile</h2>
<p>Before we delve into the Docker commands, let’s begin with a basic Dockerfile as an example, such as the following for a basic Node.js application:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2"># Use the official Node.js </span><span style="color:#BD93F9">18</span><span style="color:#F8F8F2"> Alpine image </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> a</span><span style="color:#8BE9FD;font-style:italic"> base</span></span>
<span class="line"><span style="color:#F8F8F2">FROM node:</span><span style="color:#BD93F9">18</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">alpine</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2"># Set the working directory inside the container</span></span>
<span class="line"><span style="color:#F8F8F2">WORKDIR </span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2">app</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2"># Copy the package.json and </span><span style="color:#FF79C6">package-</span><span style="color:#F8F8F2">lock.json</span></span>
<span class="line"><span style="color:#F8F8F2">COPY </span><span style="color:#FF79C6">package*</span><span style="color:#F8F8F2">.json .</span><span style="color:#FF79C6">/</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2"># Install production dependencies</span></span>
<span class="line"><span style="color:#F8F8F2">RUN npm ci</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2"># Copy the application source code</span></span>
<span class="line"><span style="color:#F8F8F2">COPY . .</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2"># Command to run the application</span></span>
<span class="line"><span style="color:#F8F8F2">CMD [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">node</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">src/index.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">]</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2"># Expose port </span><span style="color:#BD93F9">3000</span></span>
<span class="line"><span style="color:#F8F8F2">EXPOSE </span><span style="color:#BD93F9">3000</span></span></code></pre><p>A Dockerfile is a blueprint for building Docker images. It contains the commands that are executed when the image is assembled with <code>docker build</code>. This command does not start the container. Instead, it reads the Dockerfile and executes the instructions within it, resulting in a Docker image. This image contains everything needed to run your application - the base operating system, application code, dependencies, environment variables, and other configurations.</p>
<p>On the contrary, <code>docker run</code> will start a container from that image. This is when your application actually starts running. <code>docker run</code> takes the image created by <code>docker build</code> and starts a container from it. You can start, stop, and restart this container without having to rebuild the image, unless there are changes to the Dockerfile or the context that require a new image to be built.</p>
<p>Now, Let’s Explore the Docker Commands…</p>
<hr />
<h2>Building Your Image</h2>
<p>When you’re just getting started, the first step is to build your Docker image. Here’s the command I typically use:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">docker</span><span style="color:#F1FA8C"> build</span><span style="color:#F1FA8C"> .</span><span style="color:#BD93F9"> --file</span><span style="color:#F1FA8C"> Dockerfile</span><span style="color:#BD93F9"> -t</span><span style="color:#F1FA8C"> my-project</span><span style="color:#BD93F9"> --platform=linux/amd64</span></span></code></pre><p>This command builds an image from the Dockerfile in the current directory and tags it as ‘my-project’.</p>
<p>To ensure compatibility with my MacBook’s architecture, I specify the platform in the command. Additionally, I have enabled “Use Rosetta for x86/amd64 emulation on Apple Silicon” in my Docker settings. I’m using Docker Desktop 4.26.1 with engine 24.0.7.</p>
<p>If you want to see the full stack trace during the build process, you can add <code>--progress=plain</code> to the command.</p>
<p>Docker restarts the build process from where a modification was made, rather than proceeding with a full rebuild every time. This saves time and resources. However, if you need to build the image from scratch, ignoring the cache, you can use the <code>--no-cache</code> flag.</p>
<hr />
<h2>Running Your Container</h2>
<p>Once the image is built, the next step is to run it:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">docker</span><span style="color:#F1FA8C"> run</span><span style="color:#BD93F9"> -p</span><span style="color:#F1FA8C"> 127.0.0.1:5987:5987</span><span style="color:#F1FA8C"> my-project</span></span></code></pre><p>This runs the image and maps port 5987 of my local machine to port 5987 in the Docker container.</p>
<hr />
<h2>Running with Persistent State</h2>
<p>Sometimes, it’s crucial to maintain the state of your application between runs. This is especially important in scenarios where your app generates or modifies data that you want to persist. For example, imagine you’re working on a database application or a web application that stores user uploads. If you stop and restart the container, you wouldn’t want to lose all the stored data or have to re-upload files.</p>
<p>That’s where Docker volumes come in handy:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">docker</span><span style="color:#F1FA8C"> volume</span><span style="color:#F1FA8C"> create</span><span style="color:#F1FA8C"> my-state</span></span>
<span class="line"><span style="color:#50FA7B">docker</span><span style="color:#F1FA8C"> run</span><span style="color:#BD93F9"> -p</span><span style="color:#F1FA8C"> 127.0.0.1:5987:5987</span><span style="color:#BD93F9"> -v</span><span style="color:#F1FA8C"> my-state:/app/data</span><span style="color:#F1FA8C"> my-project</span></span></code></pre><p>In this command, <code>docker volume create myapp_project</code> creates a new volume named 'my-state'. When running the container, the <code>-v my-state:/app/data</code> part mounts this volume to <code>/app/data</code> inside the container. This means any data written to <code>/app/data</code> in the container is actually written to the volume on the host machine. So, when you stop and restart the container, the data persists.</p>
<p>It is worth noting that Docker manages the volume, so you might not be able to directly browse its contents through a folder using your terminal. If you’re interested in accessing these files, you can utilize the Docker Desktop app, which provides a way to explore what’s inside the volume.</p>
<hr />
<h2>Stopping Containers</h2>
<p>To stop all running containers quickly, I use:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">docker</span><span style="color:#F1FA8C"> stop</span><span style="color:#F8F8F2"> $(</span><span style="color:#50FA7B">docker</span><span style="color:#F1FA8C"> ps</span><span style="color:#BD93F9"> -aq</span><span style="color:#F8F8F2">)</span></span></code></pre><hr />
<h2>Cleaning Up</h2>
<p>And finally, when I need or want to clean everything, like removing all images and volumes:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">docker</span><span style="color:#F1FA8C"> system</span><span style="color:#F1FA8C"> prune</span><span style="color:#BD93F9"> --all</span><span style="color:#BD93F9"> --force</span><span style="color:#BD93F9"> --volumes</span></span></code></pre><p>This is especially useful when I want to start fresh or free up some space.</p>
<hr />
<p>That’s about it! These commands are the core of my Docker development routine. I hope they prove to be as beneficial for you as they have been for me.</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[A Year in Code: My Frontend Work of 2023]]></title>
          <description><![CDATA[A visual journey through the frontend projects I’ve build this year.]]></description>
          <link>https://daviddalbusco.com/blog/a-year-in-code-my-frontend-work-of-2023/</link>
          <pubDate>Sat, 30 Dec 2023 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*JThVguqwUiuW1U8rqNgvMA.png" alt="" / loading="lazy"></p>
<hr />
<p>I’ve never undertaken such an exercise before, but as another year draws to a close, I’ve realized it’s been quite a fruitful period for developing numerous frontend applications. This includes both my professional freelancing work and projects as an open-source enthusiast hobbyist.</p>
<p>In my view, all these projects have turned out exceptionally well, thanks in large part to the contributions of talented designers. So, I thought it would be a great idea to wrap up my blogging year with a brief visual summary of my work. Let’s dive in!</p>
<hr />
<h2>Juno</h2>
<p>You might find it surprising, especially if you follow me on social media, but Juno isn’t even a year old yet. Although I started the proof of concept in late 2022, it was only in February 2023, coincidentally on Valentine’s Day, that I introduced Juno to the world and opened its source code.</p>
<p>For those who don’t follow me, <a href="https://juno.build/" rel="noopener noreferrer external">Juno</a> is a zero-knowledge blockchain platform that provides developers with comprehensive tools to effortlessly create Web3 applications, akin to developing serverless Web2 apps.</p>
<p>In most of my personal projects where aesthetics are key, I collaborate with my friend Didier Renaud from <a href="https://www.customfuture.com/" rel="noopener noreferrer external">Customfuture</a> to refine and enhance the design, and Juno was no exception.</p>
<p>Summarizing Juno in a single screenshot is challenging, given its vast scope (over 1,000 commits, encompassing a website, various backend smart contracts, an admin console, a CLI, numerous examples, etc.). However, the chosen screenshot from the console encapsulates my approach: building everything from scratch, including the UI, without relying on third-party libraries. I prefer crafting my own components and have a fondness for neo-brutalism. I firmly believe that an app should make a memorable first impression on its users. I hope this one does!</p>
<p><img src="https://daviddalbusco.com/assets/images/1*Y_yuNbjrOcXHxDoYvWCfZA.png" alt="" / loading="lazy"></p>
<hr />
<h2>ckBTC</h2>
<p>This year, I had the chance to continue my contracting collaboration with the <a href="https://dfinity.org/" rel="noopener noreferrer external">DFINITY Foundation</a> and was fortunate to be part of the GIX team. We developed awesome features, notably for <a href="https://nns.internetcomputer.org/" rel="noopener noreferrer external">NNS dapp</a>, a decentralized application used for staking and participating in the <a href="https://internetcomputer.org/" rel="noopener noreferrer external">Internet Computer</a>’s governance.</p>
<p>Among the various cross-department projects, I participated in the ckBTC project, which required integration into the dapp. This was definitely just the tip of the iceberg, but it was an interesting project that required several iterations to finalize an acceptable UX.</p>
<p>One exciting thing from a frontend perspective is that we are not relying on third-party UI libraries. Instead, we have built and open-sourced our own <a href="https://gix.design/" rel="noopener noreferrer external">GIX components</a>, which can be used by our colleagues in other teams and the community. This way, we not only control our tools but also help others. It’s essentially an awesome job for anyone who’s passionate about OSS.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*X6jpzy-pu46yN1XktGIcrg.png" alt="" / loading="lazy"></p>
<hr />
<h2>icdraw</h2>
<p>I can’t recall the exact reason, but mid-year, I found myself spending a weekend creating a dapp — which I called <a href="https://icdraw.com/" rel="noopener noreferrer external">icdraw</a> — using Juno. My primary motivation, as far as I can remember, was to dogfood my tools. This practice, I believe, is an excellent way to identify pain points and bugs.</p>
<p>So, I developed a whiteboard dapp for sketching hand-drawn-like diagrams on Web3, incorporating <a href="https://excalidraw.com/" rel="noopener noreferrer external">Excalidraw</a>, a sketching tool I’m quite fond of. It was also one of my first forays into integrating Juno’s capabilities with Web Workers, as I have an aversion to ‘save’ buttons. I’m all for UX designs where the app seems to magically save data, and I enjoy crafting such seamless patterns.</p>
<p>As for the UI design, I kept it straightforward but distinctive: I added bold borders, a signature style of mine, and chose a few really vibrant background colors. After all, tools like these can never be too flashy 😉.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*XXWwTTiPloOCtXJOq0ZV4g.png" alt="" / loading="lazy"></p>
<hr />
<h2>Oisy Wallet</h2>
<p><a href="https://oisy.com/" rel="noopener noreferrer external">Oisy</a> is more than just a browser-based Ethereum wallet; it’s a testament to innovation, secured by the Internet Computer and offering networked-custody. It began as a simple idea but quickly evolved, brimming with features that each seemed too cool to leave out.</p>
<p>This wallet encompasses not just the frontend application but also a backend that securely derives keys and signs transactions. We even launched an ERC20 token, supported by an airdrop, and managed to bring everything to life in just a few weeks. Being part of a dedicated team, driven by a mission and able to deliver on such a tight schedule, is always a remarkable experience.</p>
<p>I remember that as we were under a bit of pressure to deliver, and while I was working every day, I felt like I needed a bit of fresh air. So, I took my Saturday off and went <a href="https://www.wikiloc.com/trail-running-trails/einsiedeln-tre-30k-loop-kind-of-148007378" rel="noopener noreferrer external">running 30+km</a> in the mountains next to Einsiedeln. This was also an awesome day in my running year, but that’s probably a topic for another type of blog post.</p>
<p>The design was crafted by <a href="https://twitter.com/artkorotkikh" rel="noopener noreferrer external">Artem Korotkikh</a>, with whom I enjoy collaborating. He always draws stylish, yet simple-to-understand interfaces, and the foundation is lucky to have him.</p>
<p>From a frontend perspective, there’s a lot going on, but UI-wise, it’s one of the first applications in which I began to really bet on Svelte’s transitions. While not everyone might notice it, most components are designed to transition as smoothly as possible.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*v76COgOlJolaRmGlKRM_ng.png" alt="" / loading="lazy"></p>
<hr />
<h2>Voluntary Recycling Credits</h2>
<p>The VRC (<a href="https://recyclingcredit.org/" rel="noopener noreferrer external">Voluntary Recycling Credits</a>) proof of concept is another special project we developed this year at the DFINITY Foundation, in collaboration with Roland Berger and BEEAH Group. It demonstrates how the blockchain-powered VRC standard can transform waste management by providing a turnkey solution that incentivizes recycling activities. The project was officially presented at COP28.</p>
<p>In recent years, I collaborated with ETH to create an application aimed at simulating the use of solar electricity. Therefore, on a personal note, I was really happy to be able to contribute to another project that aims to tackle environmental challenges.</p>
<p>The design was realized by Misha Iskandarov with additional contributions from Artem, and in my opinion, the outcome looks great. While one might expect a cumbersome interface for a ‘waste marketplace,’ I believe the result is user-friendly and visually appealing. As a user, it’s definitely something I would like to use.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*h4NsFyeaJz1Mb0WeoBvCsg.png" alt="" / loading="lazy"></p>
<hr />
<h2>proposals.network</h2>
<p>While using or developing on the Internet Computer (IC), especially using Juno, is great, there are always areas for improvement. Fortunately, since ICP is governed by the NNS DAO, it’s possible to submit ideas for improvements. However, one would have had to use a command line interface to do this, which is an absolute no-go for me.</p>
<p>Given the limitation mentioned above, the absence of a dapp that allows for submitting proposals for Snses through a UI, and the fact that dogfooding Juno is always beneficial, I decided to create <a href="https://proposals.network/" rel="noopener noreferrer external">proposals.network</a>.</p>
<p>This project took more than just a weekend to complete — it was more like a week of hobby work, but I’m quite pleased with the result. For the design, I employed patterns and colors from the <a href="https://neo-brutalism-ui-library.vercel.app/" rel="noopener noreferrer external">Neo-Brutalism UI</a> component library and experimented with <a href="https://tiptap.dev/" rel="noopener noreferrer external">Titap</a>. I chose to do this because a year ago, I developed <a href="https://stylojs.com/" rel="noopener noreferrer external">Stylo.js</a>, and even though I’ve essentially deprecated that project, I wanted to see how it would hold up in practice.</p>
<p>It was fascinating to note that integrating authentication, data persistence, custom domains, and even analytics was actually the quickest part of the process. I might not be doing something entirely wrong with <a href="https://juno.build/" rel="noopener noreferrer external">Juno</a>.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*pSAP0xQMaOGKD0kx5wovxg.png" alt="" / loading="lazy"></p>
<hr />
<h2>Conclusion</h2>
<p>This year has been a learning journey, filled with challenges and growth. I’m thankful for the collaborative efforts, the opportunity to work with amazing people , and the shared passion that made these projects possible.</p>
<p>Looking ahead, I’m excited to see what the next year brings. Here’s to continued collaboration, learning, and coding in the ever-evolving world of web development.</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Data-Loading for Docusaurus Routes]]></title>
          <description><![CDATA[A simple guide to building routes and loading data in Docusaurus projects, illustrated by a showcase example.]]></description>
          <link>https://daviddalbusco.com/blog/data-loading-for-docusaurus-routes/</link>
          <pubDate>Sun, 10 Dec 2023 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*9d7swF0OBzT89QNTE-lEWA.png" alt="" / loading="lazy"></p>
<hr />
<p>This weekend, I started creating a showcase of all dapps built with <a href="https://juno.build/showcase" rel="noopener noreferrer external">Juno</a> for our website, which uses Docusaurus. Since I found the documentation somewhat lacking, I’ve decided to share a tutorial outlining the steps involved.</p>
<blockquote>
<p>Note: All the code discussed in this blog post is open source and available for you to view and use. You can find it in my repository <a href="https://github.com/junobuild/docs" rel="noopener noreferrer external">here</a>.</p>
</blockquote>
<hr />
<h2>UI: Page</h2>
<p>If your primary interest is in data loading, this step might be optional. However, for building a feature like a “showcase,” it’s helpful to start with creating a new route. For my project, where I wanted to present the page at a /showcase path, I initated a new component in src/pages/showcase/index.tsx, using TypeScript/TSX.</p>
<p>The layout of this page is simple. It’s composed of two sections: the first serves as an introduction and includes an invitation for submissions of dapps to be featured in the showcase. The second section is where the spotlighted applications will be displayed.</p>
<p>Remember, at this point, the component doesn’t contain any data. It’s essentially a skeleton that we’ll populate with data in the following chapters.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> Link </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@docusaurus/Link</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> Spotlight </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@site/src/components/Spotlight</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> Layout </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@theme/Layout</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> styles </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./index.module.scss</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> Showcase</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#6272A4">    // TODO: the data we want to display</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> dapps </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> [];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Layout</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">section</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">>Showcase&#x3C;/</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">>List of dApps devs are building with Juno.&#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Link</span><span style="color:#50FA7B;font-style:italic"> href</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://github.com/junobuild/docs#submit-your-work</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> target</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">_blank</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                    🙏 Please add yours</span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">Link</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#FF79C6">section</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">section</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> className</span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2">{styles.grid}></span></span>
<span class="line"><span style="color:#F8F8F2">                    {dapps.</span><span style="color:#50FA7B">map</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">data</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">i</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">                        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Spotlight</span><span style="color:#F8F8F2"> {</span><span style="color:#FF79C6">...</span><span style="color:#F8F8F2">data} </span><span style="color:#50FA7B;font-style:italic">key</span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2">{i} /></span></span>
<span class="line"><span style="color:#F8F8F2">                    ))}</span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#FF79C6">section</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">Layout</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>The snippet shared earlier focuses on the HTML structure and doesn’t include styling details. For those interested in the visual layout of the cards that we will populate, here’s a quick overview of the CSS. It’s designed to display a grid layout on wider devices and switch to a flex column layout on mobile devices.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B;font-style:italic">.grid</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD">    display</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> flex</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">    flex-direction</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> column</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    --grid-gap</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 2</span><span style="color:#FF79C6">rem</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">    gap</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD"> var</span><span style="color:#F8F8F2">(--grid-gap);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    @media screen and (</span><span style="color:#8BE9FD">min-width</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 768</span><span style="color:#FF79C6">px</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#8BE9FD">        display</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> grid</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">        grid-template-columns</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD"> repeat</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">2</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD">calc</span><span style="color:#F8F8F2">((</span><span style="color:#BD93F9">100</span><span style="color:#FF79C6">%</span><span style="color:#FF79C6"> -</span><span style="color:#F8F8F2"> (</span><span style="color:#BD93F9">1</span><span style="color:#FF79C6"> *</span><span style="color:#8BE9FD"> var</span><span style="color:#F8F8F2">(--grid-gap))) / </span><span style="color:#BD93F9">2</span><span style="color:#F8F8F2">));</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    @media</span><span style="color:#BD93F9"> screen</span><span style="color:#FF79C6"> and</span><span style="color:#F8F8F2"> (</span><span style="color:#8BE9FD">min-width</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 996</span><span style="color:#FF79C6">px</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        grid-template-columns: repeat(3</span><span style="color:#F8F8F2">,</span><span style="color:#FF79C6"> calc((100% - (3 * var(--grid-gap</span><span style="color:#F8F8F2">))) / 3));</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h2>UI: Component</h2>
<p>In this chapter, we’re going to look at the component that displays the dapps developers have built. The showcase page is set up to list these projects. To handle this, I needed a specific component.</p>
<p>Given that the project is built with TypeScript, I created an interface to define the metadata for the dapps. This interface is helpful for the process of loading, listing, and displaying the data, ensuring that the information about each project is organized and clear.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> interface</span><span style="color:#8BE9FD;font-style:italic"> ShowcaseSpotlight</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    title</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    url</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    description</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    img</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Following the definition of the interface, the next step is to create the component src/components/Spotlight/index.tsx that will render the information. This component will use the structure provided by the interface to display the details of each dapp in the showcase effectively and consistently.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> Link </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@docusaurus/Link</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { ShowcaseSpotlight } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@site/src/types/showcase</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> styles </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./index.module.scss</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> Spotlight</span><span style="color:#F8F8F2">({ </span><span style="color:#FFB86C;font-style:italic">title</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">description</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">img</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">url</span><span style="color:#F8F8F2"> }</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> ShowcaseSpotlight</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Link</span><span style="color:#50FA7B;font-style:italic"> href</span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2">{url} </span><span style="color:#50FA7B;font-style:italic">target</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">_blank</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> rel</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">noreferrer noopener nofollow</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">article</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;</span><span style="color:#FF79C6">img</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                        src</span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2">{img}</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                        loading</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">lazy</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                        className</span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2">{styles.img}</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                        role</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">presentation</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                        alt</span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2">{</span><span style="color:#F1FA8C">`</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">title</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C"> banner`</span><span style="color:#F8F8F2">}</span></span>
<span class="line"><span style="color:#F8F8F2">                    /></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;</span><span style="color:#FF79C6">h4</span><span style="color:#F8F8F2">>{title}&#x3C;/</span><span style="color:#FF79C6">h4</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">>{description}&#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#FF79C6">article</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">Link</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h2>Data</h2>
<p>At this stage in the development, the UI is almost complete, except for the actual receipt of the data. The next logical step is to establish a source for this data. Considering that the website is pre-rendered and that dapps are submitted to the showcase via pull requests, opting for a JSON file at the root of the repository made sense.</p>
<p>This JSON source file aligns with the structure of the data as defined by the interface we declared in the previous chapter.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">[</span></span>
<span class="line"><span style="color:#F8F8F2">    {</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">title</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Windoge98</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">url</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">https://windoge98.com</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">description</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">An art/memecoin launched on the Internet Computer Protocol (ICP).</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">img</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">/showcase/windoge98.jpg</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    },</span></span>
<span class="line"><span style="color:#F8F8F2">    {</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">title</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Spellkaster</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">url</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">https://spellkaster.app</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">description</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Cast spells on internet computer things 🪄.</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">img</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">/showcase/spellkaster.webp</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">]</span></span></code></pre><hr />
<h2>Plugin</h2>
<p>With the source data defined and the UI set up, there’s one crucial link missing: loading the data and injecting it into the routes.</p>
<p>Initially, I considered simply importing the JSON file directly into my component, and this was my first approach. However, after some reflection, I decided to adopt a more static site generation approach by developing a Docusaurus plugin.</p>
<p>Docusaurus provides the flexibility to write custom plugins and hook into its <a href="https://docusaurus.io/docs/api/plugin-methods/lifecycle-apis" rel="noopener noreferrer external">lifecycle APIs</a>. These APIs allow for loading and populating data when the site is built, aligning perfectly with our needs.</p>
<p>Following this approach, I created a file named docusaurus.showcase.plugin.ts. This plugin takes advantage of two key lifecycle methods: loadContent() and contentLoaded().</p>
<p>The loadContent() method is used to fetch data from our source, in this case, reading the showcase.json file. The contentLoaded() method then takes this data and populates it to the route of our choice.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { join } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">node:path</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { readFileSync } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">node:fs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> source </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> join</span><span style="color:#F8F8F2">(process.</span><span style="color:#50FA7B">cwd</span><span style="color:#F8F8F2">(), </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">showcase.json</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> showcasePlugin</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">context</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">options</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        name</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">docusaurus-dapps-plugin</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#FF79C6">        async</span><span style="color:#50FA7B"> loadContent</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#50FA7B"> readFileSync</span><span style="color:#F8F8F2">(source, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">utf-8</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">        },</span></span>
<span class="line"><span style="color:#FF79C6">        async</span><span style="color:#50FA7B"> contentLoaded</span><span style="color:#F8F8F2">({ </span><span style="color:#FFB86C;font-style:italic">content</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">actions</span><span style="color:#F8F8F2"> }) {</span></span>
<span class="line"><span style="color:#FF79C6">            const</span><span style="color:#F8F8F2"> { createData, addRoute } </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> actions;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">            const</span><span style="color:#F8F8F2"> dappsJsonPath </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> createData</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">dapps.json</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, content);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">            addRoute</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">                path</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">/showcase</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">                component</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@site/src/pages/showcase/index.tsx</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">                modules</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">                    dapps</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> dappsJsonPath</span></span>
<span class="line"><span style="color:#F8F8F2">                },</span></span>
<span class="line"><span style="color:#F8F8F2">                exact</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span></span>
<span class="line"><span style="color:#F8F8F2">            });</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>The key aspect of the plugin functionality lies in the addRoute part. This is a crucial step because if it's not configured correctly, the data won't load as it should. The addRoute function demands specific details: first, it needs the route where the data should be loaded, which in your case is /showcase. Next, it requires specifying the target component. Given the use of TypeScript in your project, this corresponds to the page defined earlier: @site/src/pages/showcase/index.tsx.</p>
<p>The process also involves the createData function, which is responsible for creating the data set. After creating the data, it's added to the module with a specific identifier, here labeled as dapps. This identifier is significant and will be crucial in the final chapter, where you will complete the integration.</p>
<hr />
<h2>Build: Use the plugin</h2>
<p>Before finalizing our solution, there’s one step remaining in the build system: informing Docusaurus to utilize our custom plugin. This is accomplished by adding a new entry in the plugins array within the docusaurus.config.ts configuration file.</p>
<p>It’s worth noting that this step doesn’t require any special imports. You simply need to add a plain string to the plugins list, pointing to the file where the plugin is located.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> config</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Config</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#6272A4">  // Placeholder ... for other stuffs within the Config</span></span>
<span class="line"><span style="color:#FF79C6">  ...</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2"> plugins: [</span></span>
<span class="line"><span style="color:#FF79C6">    ...</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    join</span><span style="color:#F8F8F2">(process.</span><span style="color:#50FA7B">cwd</span><span style="color:#F8F8F2">(), </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">docusaurus.showcase.plugin.ts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">),</span></span>
<span class="line"><span style="color:#F8F8F2">  ],</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  ...</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">module</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">exports</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> config;</span></span></code></pre><hr />
<h2>UI: Receive and use the data</h2>
<p>With the plugin now a part of the build system, the last step involves our UI component receiving and using the data. The key here is the identifier we chose in the plugin, which is dapps.</p>
<p>In the component at @site/src/pages/showcase/index.tsx, we can easily access the loaded data. The data from our plugin is passed into the component as properties. That’s it.</p>
<p>By simply using the dapps data in our component, we complete the setup, enabling the showcase page to display the latest information about the dapps submitted.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> React </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> styles </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./index.module.scss</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> Link </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@docusaurus/Link</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> Spotlight </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@site/src/components/Spotlight</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> Layout </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@theme/Layout</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> Showcase</span><span style="color:#F8F8F2">({ </span><span style="color:#FFB86C;font-style:italic">dapps</span><span style="color:#F8F8F2"> }) {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Layout</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">section</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">>Showcase&#x3C;/</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">>List of dApps devs are building with Juno.&#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Link</span><span style="color:#50FA7B;font-style:italic"> href</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://github.com/junobuild/docs#submit-your-work</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> target</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">_blank</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                    🙏 Please add yours</span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">Link</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#FF79C6">section</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">section</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> className</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">styles.grid</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#FF79C6">                    {</span><span style="color:#F8F8F2">dapps.</span><span style="color:#50FA7B">map</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">data</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">i</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">                        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Spotlight</span><span style="color:#FF79C6"> {...</span><span style="color:#F8F8F2">data</span><span style="color:#FF79C6">}</span><span style="color:#50FA7B;font-style:italic"> key</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">i</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">                    ))</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#FF79C6">section</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">Layout</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h2>Conclusion</h2>
<p>This tutorial guided us through building a dapps showcase using Docusaurus. From setting up the UI to integrating a custom plugin for data handling, each step contributed to the final, dynamic showcase. I hope this straightforward approach helps in your own Docusaurus projects.</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Essential JavaScript Functions for Detecting User’s Device Characteristics]]></title>
          <description><![CDATA[Learn how to detect iOS, Android, fullscreen mode, Firefox, Safari, and more with JavaScript.]]></description>
          <link>https://daviddalbusco.com/blog/essential-javascript-functions-for-detecting-users-device-characteristics/</link>
          <pubDate>Sun, 12 Nov 2023 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*dQT7UMbmwnLQNoV4Z0Uqzg.jpeg" alt="" / loading="lazy"></p>
<p>Photo by <a href="https://unsplash.com/fr/@bramnaus?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash" rel="noopener noreferrer external">Bram Naus</a> on <a href="https://unsplash.com/fr/photos/macbook-argento-accanto-a-iphone-6-grigio-siderale-e-bicchiere-trasparente-su-ripiano-in-legno-marrone-n8Qb1ZAkK88?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash" rel="noopener noreferrer external">Unsplash</a></p>
<hr />
<p>Time and again, I’ve encountered the need to implement features in my web-based projects that specifically target certain types of devices. Each time, I find myself falling back on the same set of utilities I’ve been using for years. It’s high time I shared these invaluable tools in a blog post.</p>
<p>In this article, you’ll gain access to a curated collection of JavaScript functions for identifying iOS and Android devices, detecting fullscreen mode, distinguishing Firefox from Safari, and more.</p>
<blockquote>
<p>It’s important to be aware that some of these methods are based on parsing the navigator user-agent string, which might see changes in the future as Google plans to <a href="https://developer.chrome.com/blog/user-agent-reduction-deprecation-trial/" rel="noopener noreferrer external">reduce the information</a> provided by this string.</p>
</blockquote>
<hr />
<h2>isMobile</h2>
<p>Let’s start with the detection of a mobile device. I determine if a user is using my applications in a browser-based environment that might be a mobile device by testing whether the user is using a pointing device with limited accuracy, such as a touch screen, and which is not a precise device such as a mouse.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> isMobile</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#FF79C6"> =></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> isTouchScreen </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> window.</span><span style="color:#50FA7B">matchMedia</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">(any-pointer:coarse)</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">).matches;</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> isMouseScreen </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> window.</span><span style="color:#50FA7B">matchMedia</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">(any-pointer:fine)</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">).matches;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> isTouchScreen </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#FF79C6"> !</span><span style="color:#F8F8F2">isMouseScreen;</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>The <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia" rel="noopener noreferrer external">matchMedia</a> interface returns an object that can be used to determine if the document or web page matches a specific media query. To test the pointing device, I utilize the <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/any-pointer" rel="noopener noreferrer external">any-pointer</a> CSS media feature.</p>
<hr />
<h2>isIPad</h2>
<p>Identifying an iPad device involves inspecting the user-agent, but it also necessitates combining this data with the previous mobile detection function. This is because Safari presents a string similar to that of desktop or laptop devices.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> isIPad</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#FF79C6"> =></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        navigator</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { userAgent }</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> window;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // iOS 12 and below</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF5555">/</span><span style="color:#F1FA8C">iPad</span><span style="color:#FF5555">/</span><span style="color:#FF79C6">i</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">test</span><span style="color:#F8F8F2">(userAgent)) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // iOS 13+</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#FF5555"> /</span><span style="color:#F1FA8C">Macintosh</span><span style="color:#FF5555">/</span><span style="color:#FF79C6">i</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">test</span><span style="color:#F8F8F2">(userAgent) </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#50FA7B"> isMobile</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>On older versions of iPadOS, the user-agent used to include the indentation; therefore, I also check if the string might contain this information.</p>
<blockquote>
<p>Note that the <code>i</code> in the regex showcased in this article stands for <code>insensitive</code> meaning it performs a test without considering case sensitivity.</p>
</blockquote>
<hr />
<h2>isIPhone</h2>
<p>I combine iPhone and iPod into a single function because their user experience is similar for my projects. Both can be tested by querying the user-agent, which contains their indication.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> isIPhone</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#FF79C6"> =></span><span style="color:#FF5555"> /</span><span style="color:#F1FA8C">iPhone</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">iPod</span><span style="color:#FF5555">/</span><span style="color:#FF79C6">i</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">test</span><span style="color:#F8F8F2">(window.navigator.userAgent);</span></span></code></pre><hr />
<h2>isIOS</h2>
<p>I assume you already know the answer: to detect if a device is running iOS, we can simply use the two previous functions.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> isIOS</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#FF79C6"> =></span><span style="color:#50FA7B"> isIPhone</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">||</span><span style="color:#50FA7B"> isIPad</span><span style="color:#F8F8F2">();</span></span></code></pre><h2>isAndroid</h2>
<p>Similar to iPhones, detecting Android devices is straightforward because the necessary information is provided in the user-agent string.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> isAndroid</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#FF79C6"> =></span><span style="color:#FF5555"> /</span><span style="color:#F1FA8C">android</span><span style="color:#FF5555">/</span><span style="color:#FF79C6">i</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">test</span><span style="color:#F8F8F2">(window.navigator.userAgent);</span></span></code></pre><hr />
<h2>isAndroidTablet</h2>
<p>According to my tests, an Android tablet is an Android device, which is strangely not identified by the keyword <code>mobile</code> in its user-agent string.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> isAndroidTablet</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#FF79C6"> =></span><span style="color:#50FA7B"> isAndroid</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#FF79C6"> !</span><span style="color:#FF5555">/</span><span style="color:#F1FA8C">mobile</span><span style="color:#FF5555">/</span><span style="color:#FF79C6">i</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">test</span><span style="color:#F8F8F2">(window.navigator.userAgent);</span></span></code></pre><hr />
<h2>isPortrait</h2>
<p>Once, I also had to determine if the user’s device was held in portrait mode. Instead of comparing screen dimensions, I utilized the <code>matchMedia</code> interface to determine the orientation of the device.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> isPortrait</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#FF79C6"> =></span><span style="color:#F8F8F2"> window.</span><span style="color:#50FA7B">matchMedia</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">(orientation: portrait)</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">).matches;</span></span></code></pre><hr />
<h2>isLandscape</h2>
<p>Similarly, we can implement the opposite of the previous function to determine if the device’s orientation is in landscape mode.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> isLandscape</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#FF79C6"> =></span><span style="color:#FF79C6"> !</span><span style="color:#50FA7B">isPortrait</span><span style="color:#F8F8F2">();</span></span></code></pre><hr />
<h2>isFullscreen</h2>
<p>To determine if a browser is used in fullscreen mode, I typically use the document <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/fullscreenElement" rel="noopener noreferrer external">fullscreenElement</a> property, but I also check vendor-specific properties that are supposed to reflect the same information.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> isFullscreen</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#FF79C6"> =></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#6272A4">        // @ts-ignore</span></span>
<span class="line"><span style="color:#6272A4">        // prettier-ignore</span></span>
<span class="line"><span style="color:#FF79C6">        !!</span><span style="color:#F8F8F2">(document.fullscreenElement </span><span style="color:#FF79C6">||</span><span style="color:#F8F8F2"> document.mozFullScreenElement </span><span style="color:#FF79C6">||</span><span style="color:#F8F8F2"> document.webkitFullscreenElement </span><span style="color:#FF79C6">||</span><span style="color:#F8F8F2"> document.msFullscreenElement)</span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><hr />
<h2>isSafari</h2>
<p>While the navigator <a href="https://developer.mozilla.org/en-US/docs/Web/API/Navigator/vendor" rel="noopener noreferrer external">vendor</a> property is marked as deprecated by MDN, I still rely on it to check if a browser is Safari.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> isSafari</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#FF79C6"> =></span><span style="color:#FF5555"> /</span><span style="color:#F1FA8C">apple</span><span style="color:#FF5555">/</span><span style="color:#FF79C6">i</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">test</span><span style="color:#F8F8F2">(navigator.vendor);</span></span></code></pre><p>You might argue that this would also return true on an iPhone when, for example, Chrome is used. To that, I would offer a friendly reminder that any browser running on an iPhone or iPad is, unfortunately, still Safari due to Apple’s vendor lockdown monopoly.</p>
<hr />
<h2>isFirefox</h2>
<p>Knowing if an application is running in a Firefox browser can be determined by checking the user-agent string.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> isFirefox</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#FF79C6"> =></span><span style="color:#FF5555"> /</span><span style="color:#F1FA8C">firefox</span><span style="color:#FF5555">/</span><span style="color:#FF79C6">i</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">test</span><span style="color:#F8F8F2">(window.navigator.userAgent);</span></span></code></pre><hr />
<h2>isRTL</h2>
<p>Finally, if your application focuses on internationalization, this is how I determine if the content should be read from right to left. I set the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/dir" rel="noopener noreferrer external">dir</a> attribute at the top level of the document.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> isRTL</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#FF79C6"> =></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> htmlDir </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> document.documentElement.</span><span style="color:#50FA7B">getAttribute</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">dir</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> htmlDir </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">rtl</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>To infinity and beyond.<br />
David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[$: npm publish -if-needed]]></title>
          <description><![CDATA[Publish a new version of your libraries on NPM only if changes have been made.]]></description>
          <link>https://daviddalbusco.com/blog/npm-publish-if-needed/</link>
          <pubDate>Fri, 27 Oct 2023 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*KYD5_FMmuJCIbp2--hBcvA.jpeg" alt="" / loading="lazy"></p>
<p>Photo by <a href="https://unsplash.com/fr/@fakurian?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash" rel="noopener noreferrer external">Milad Fakurian</a> on <a href="https://unsplash.com/fr/photos/tableau-abstrait-rose-vert-et-bleu-UYgrVfIhBec?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash" rel="noopener noreferrer external">Unsplash</a></p>
<hr />
<p>I’m not particularly strong in DevOps, but a few weeks ago, I found myself working on improving a monorepo that contains multiple JavaScript libraries published to NPM. Until then, we had been publishing all libraries with every release, whether they were modified or not. Surprisingly, there wasn’t an option to skip unmodified libraries. So, inspired by a GitHub <a href="https://github.com/npm/rfcs/issues/466" rel="noopener noreferrer external">thread</a>, I decided to implement a small script that accomplishes this task. In this short blog post, I’m sharing the script with you.</p>
<h2>Script</h2>
<p>The script that publishes a library only if it has been modified is as follows.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">#!/usr/bin/env bash</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">function</span><span style="color:#50FA7B"> publish_npm</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">  local</span><span style="color:#BD93F9"> lib</span><span style="color:#FF79C6">=</span><span style="color:#FFB86C;font-style:italic">$1</span></span>
<span class="line"></span>
<span class="line"><span style="color:#BD93F9">  LOCAL_SHASUM</span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2">$(</span><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> pack</span><span style="color:#BD93F9"> -w</span><span style="color:#F1FA8C"> packages/</span><span style="color:#E9F284">"</span><span style="color:#BD93F9">$lib</span><span style="color:#E9F284">"</span><span style="color:#BD93F9"> --json</span><span style="color:#FF79C6"> |</span><span style="color:#50FA7B"> jq</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">.[] | .shasum</span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> |</span><span style="color:#50FA7B"> sed</span><span style="color:#BD93F9"> -r</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">s/^"|"$//g</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#BD93F9">  NPM_TARBALL</span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2">$(</span><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> show</span><span style="color:#F1FA8C"> @dfinity/</span><span style="color:#E9F284">"</span><span style="color:#BD93F9">$lib</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C"> dist.tarball</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#BD93F9">  NPM_SHASUM</span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2">$(</span><span style="color:#50FA7B">curl</span><span style="color:#BD93F9"> -s</span><span style="color:#E9F284"> "</span><span style="color:#BD93F9">$NPM_TARBALL</span><span style="color:#E9F284">"</span><span style="color:#FF79C6"> 2>&#x26;1</span><span style="color:#FF79C6"> |</span><span style="color:#50FA7B"> shasum</span><span style="color:#FF79C6"> |</span><span style="color:#50FA7B"> cut</span><span style="color:#BD93F9"> -f1</span><span style="color:#BD93F9"> -d</span><span style="color:#E9F284">'</span><span style="color:#E9F284"> '</span><span style="color:#F8F8F2">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> [ </span><span style="color:#E9F284">"</span><span style="color:#BD93F9">$LOCAL_SHASUM</span><span style="color:#E9F284">"</span><span style="color:#FF79C6"> ==</span><span style="color:#E9F284"> "</span><span style="color:#BD93F9">$NPM_SHASUM</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> ]; </span><span style="color:#FF79C6">then</span></span>
<span class="line"><span style="color:#8BE9FD">    echo</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">No changes in </span><span style="color:#BD93F9">$lib</span><span style="color:#F1FA8C"> need to be published to NPM.</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#FF79C6">  else</span></span>
<span class="line"><span style="color:#50FA7B">    npm</span><span style="color:#F1FA8C"> publish</span><span style="color:#BD93F9"> --workspace=packages/</span><span style="color:#E9F284">"</span><span style="color:#BD93F9">$lib</span><span style="color:#E9F284">"</span><span style="color:#BD93F9"> --provenance</span><span style="color:#BD93F9"> --access</span><span style="color:#F1FA8C"> public</span></span>
<span class="line"><span style="color:#FF79C6">  fi</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4"># Tips: libs use by other libs first</span></span>
<span class="line"><span style="color:#BD93F9">LIBS</span><span style="color:#FF79C6">=</span><span style="color:#F1FA8C">utils,ledger-icrc,ledger-icp,nns-proto,nns,sns,cmc,ckbtc,ic-management</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">for</span><span style="color:#BD93F9"> lib</span><span style="color:#FF79C6"> in</span><span style="color:#F8F8F2"> $(</span><span style="color:#8BE9FD">echo</span><span style="color:#BD93F9"> $LIBS</span><span style="color:#FF79C6"> |</span><span style="color:#50FA7B"> sed</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">s/,/ /g</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">); </span><span style="color:#FF79C6">do</span></span>
<span class="line"><span style="color:#50FA7B">  publish_npm</span><span style="color:#E9F284"> "</span><span style="color:#BD93F9">$lib</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#FF79C6">done</span></span></code></pre><p>So, what does it do?</p>
<p>First, I use a list of libraries that should be published, as seen in the variable <code>LIBS</code>. I could have simply listed all the packages present in the workspace with a bash command, but there might be some folders that should be skipped. Therefore, I decided to use a manual list.</p>
<blockquote>
<p>Note that I am using <a href="https://docs.npmjs.com/cli/v7/using-npm/workspaces" rel="noopener noreferrer external">npm workspaces</a>.</p>
</blockquote>
<p>For each of these libraries, a function called <code>publish_npm</code> is executed.</p>
<p>To begin, the function calculates the shasum of the library as if it were to be published. It accomplishes this by using the npm command <code>pack</code> with the <code>--json</code> option. This command generates JSON output, including various information:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">[</span></span>
<span class="line"><span style="color:#F8F8F2">    {</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">id</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@dfinity/utils@1.0.0</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">name</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@dfinity/utils</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">version</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">1.0.0</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">size</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 35763</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">unpackedSize</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 124014</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">shasum</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">86f07bc3846e775ebc641486ff963f7b12e06476</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">integrity</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">sha512-xpsAgsx5jJZcdIYVbSHJ9XiNTtNIEzu8PH6Z2ALfYzF5an6B1HHTOGmhGUfLFBiEbQAyGb0zULcqBUV6lFVlCg==</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">filename</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">dfinity-utils-1.0.0.tgz</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">files</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> []</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">]</span></span></code></pre><p>As you can see, it displays the shasum of the library. Therefore, I use <code>jq</code> and <code>sed</code> to extract this value into a variable.</p>
<p>Next, the function uses the <code>npm show</code> command to fetch the URL of the tarball hosted on npm:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">❯</span><span style="color:#F1FA8C"> npm</span><span style="color:#F1FA8C"> show</span><span style="color:#F1FA8C"> @dfinity/utils</span><span style="color:#F1FA8C"> dist.tarball</span></span>
<span class="line"><span style="color:#50FA7B">https://registry.npmjs.org/@dfinity/utils/-/utils-1.0.0.tgz</span></span></code></pre><p>With this URL, the script can download the current version of the library without saving it to the filesystem. It does this by piping the data to the <code>shasum</code> command, allowing it to compute the current sha:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">❯</span><span style="color:#F1FA8C"> curl</span><span style="color:#BD93F9"> -s</span><span style="color:#F1FA8C"> https://registry.npmjs.org/@dfinity/utils/-/utils-1.0.0.tgz</span><span style="color:#FF79C6"> 2>&#x26;1</span><span style="color:#FF79C6"> |</span><span style="color:#50FA7B"> shasum</span><span style="color:#FF79C6"> |</span><span style="color:#50FA7B"> cut</span><span style="color:#BD93F9"> -f1</span><span style="color:#BD93F9"> -d</span><span style="color:#E9F284">'</span><span style="color:#E9F284"> '</span></span>
<span class="line"><span style="color:#50FA7B">86f07bc3846e775ebc641486ff963f7b12e06476</span></span></code></pre><p>Finally, the script compares both the local and remote shasums. If they are different, it proceeds to publish the new version to npm, as a difference in the two shasums indicates that the local library has been modified.</p>
<hr />
<h2>Integration in GitHub Actions</h2>
<p>Now that you have the script, here’s how you can integrate it into a GitHub Actions workflow that builds and publishes to npm each time you release your monorepo.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#8BE9FD">name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> Publish</span></span>
<span class="line"></span>
<span class="line"><span style="color:#BD93F9">on</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">  release</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">    types</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#F1FA8C">released</span><span style="color:#F8F8F2">]</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">jobs</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">  build</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">    runs-on</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> ubuntu-latest</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">    permissions</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">      contents</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> read</span></span>
<span class="line"><span style="color:#8BE9FD">      id-token</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> write</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">    steps</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#FF79C6">      -</span><span style="color:#8BE9FD"> uses</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> actions/checkout@v3</span></span>
<span class="line"><span style="color:#FF79C6">      -</span><span style="color:#8BE9FD"> uses</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> actions/setup-node@v3</span></span>
<span class="line"><span style="color:#8BE9FD">        with</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">          node-version</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">18.x</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#8BE9FD">          registry-url</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">https://registry.npmjs.org</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#FF79C6">      -</span><span style="color:#8BE9FD"> run</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> npm install -g npm</span></span>
<span class="line"><span style="color:#FF79C6">      -</span><span style="color:#8BE9FD"> run</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> npm ci</span></span>
<span class="line"><span style="color:#FF79C6">      -</span><span style="color:#8BE9FD"> run</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> npm run build --workspaces</span></span>
<span class="line"><span style="color:#FF79C6">      -</span><span style="color:#8BE9FD"> name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> Publish</span></span>
<span class="line"><span style="color:#8BE9FD">        run</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> ./scripts/publish-npm.sh</span></span>
<span class="line"><span style="color:#8BE9FD">        env</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">          NODE_AUTH_TOKEN</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> ${{secrets.NPM_TOKEN}}</span></span></code></pre><p>This GitHub Actions workflow is configured to execute the NPM publishing script when a release is created in your repository. It checks out your code, sets up Node.js, installs dependencies, builds the project, and then runs our custom script, ensuring that libraries are selectively published only when changes have occurred. The <code>NODE_AUTH_TOKEN</code> secret is used to authenticate with npm during the publishing process.</p>
<p>To infinity and beyond.<br />
David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[A Sass Mixin to Build Your Own CSS Framework à la Tailwind]]></title>
          <description><![CDATA[Ready to craft a Sass mixin to generate your own class-based CSS approach for your app?]]></description>
          <link>https://daviddalbusco.com/blog/a-sass-mixin-to-build-your-own-css-framework-a-la-tailwind/</link>
          <pubDate>Sat, 30 Sep 2023 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*KYkxKsaCqV1uq8Sxfmyykw.jpeg" alt="" / loading="lazy"></p>
<p>Photo by <a href="https://unsplash.com/fr/@dorner?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Mike Dorner</a> on <a href="https://unsplash.com/fr/photos/sf_1ZDA1YFw?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></p>
<hr />
<p>In a recent project I built from scratch, I was intrigued by the idea of experimenting with chunking my app to optimize its performance when deployed on the blockchain. One of my primary goals was to minimize the number of CSS files included in the project. As I explored this further, I had the idea of using components with a class-based approach similar to Tailwind CSS. However, I steered clear of relying on any external frameworks, as I’ve always been cautious about adding unnecessary dependencies if they aren’t required. I also believed that this experiment could provide greater flexibility in the long run.</p>
<p>In a nutshell, I ended up creating a Sass mixin for building my own class-based framework. In this blog post, I’m excited to share it with you.</p>
<blockquote>
<p>The project we built together with my colleagues is called <a href="https://oisy.com/" rel="noopener noreferrer external">Oisy</a>. It’s an <a href="https://github.com/dfinity/oisy-wallet" rel="noopener noreferrer external">open-source</a> Ethereum wallet hosted on the <a href="https://internetcomputer.org/" rel="noopener noreferrer external">Internet Computer</a>.</p>
</blockquote>
<hr />
<h2>Overview</h2>
<p>The idea behind the mixin is to eliminate code duplication while enabling the generation of classes that can be used to create responsive components. For instance, consider the following code snippet:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> class</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">flex justify-center gap-1 mt-4</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">span</span><span style="color:#50FA7B;font-style:italic"> class</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">text-blue</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Hello&#x3C;/</span><span style="color:#FF79C6">span</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">span</span><span style="color:#50FA7B;font-style:italic"> class</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">text-cyclamen font-bold</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>World&#x3C;/</span><span style="color:#FF79C6">span</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span></code></pre><p>This code would render a web page featuring a <code>div</code> container with a centered flex layout and a top margin. Inside this container, there are two inline <code>span</code> elements, each assigned specific colors, with one of them set to bold text.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*dkHS4VctREJmFtyftvFS_w.png" alt="" / loading="lazy"></p>
<p>While I could have simply declared these classes globally, my objective was to generate them using a Sass utility for all my styling needs.</p>
<hr />
<h2>Gotcha</h2>
<p>This solution exceeded my expectation when I first began to code it but, it’s essential to note that it’s not as advanced as dedicated CSS frameworks like Tailwind. Before we proceed further, it’s important to be aware of the following two limitations:</p>
<ol>
<li><p>Responsivity in this solution is limited to the usage <strong>one breakpoint</strong>. Throughout the development of the application, UI rules consistently relied on a single breakpoint. However, there were instances when experimenting with multiple breakpoints, the visual outcomes didn’t always align with expectations. If your requirements involve multiple breakpoints, the mixin may require improvements in terms of selectors to accommodate these scenarios.</p></li>
<li><p>It’s common for CSS frameworks to include a <code>postCss</code> job that handles the task of cleaning up your bundle by removing any unused styles after compilation. However, I do not provide such a hook here.</p></li>
</ol>
<hr />
<h2>Media Queries</h2>
<p>While not strictly necessary for the implementation of this solution, I leveraged another mixin we use across all our applications. Within a <code>_media.scss</code> mixin file, I incorporated the following code to ease the utilization of breakpoint rules before effectively implementing the core of this tutorial:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">$breakpoint-xsmall</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 320</span><span style="color:#FF79C6">px</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">$breakpoint-small</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 576</span><span style="color:#FF79C6">px</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">$breakpoint-medium</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 768</span><span style="color:#FF79C6">px</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">$breakpoint-large</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 1024</span><span style="color:#FF79C6">px</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">$breakpoint-extra-large</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 1300</span><span style="color:#FF79C6">px</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">@mixin</span><span style="color:#50FA7B"> min-width</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">$breakpoint</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">    @if</span><span style="color:#F8F8F2"> ($breakpoint == xsmall) {</span></span>
<span class="line"><span style="color:#FF79C6">        @media</span><span style="color:#F8F8F2"> (</span><span style="color:#8BE9FD">min-width</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> $breakpoint-xsmall) {</span></span>
<span class="line"><span style="color:#FF79C6">            @content</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">@else if</span><span style="color:#F8F8F2"> ($breakpoint == </span><span style="color:#BD93F9">small</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        @media</span><span style="color:#F8F8F2"> (</span><span style="color:#8BE9FD">min-width</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> $breakpoint-small) {</span></span>
<span class="line"><span style="color:#FF79C6">            @content</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">@else if</span><span style="color:#F8F8F2"> ($breakpoint == </span><span style="color:#BD93F9">medium</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        @media</span><span style="color:#F8F8F2"> (</span><span style="color:#8BE9FD">min-width</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> $breakpoint-medium) {</span></span>
<span class="line"><span style="color:#FF79C6">            @content</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">@else if</span><span style="color:#F8F8F2"> ($breakpoint == </span><span style="color:#BD93F9">large</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        @media</span><span style="color:#F8F8F2"> (</span><span style="color:#8BE9FD">min-width</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> $breakpoint-large) {</span></span>
<span class="line"><span style="color:#FF79C6">            @content</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">@else if</span><span style="color:#F8F8F2"> ($breakpoint == xlarge) {</span></span>
<span class="line"><span style="color:#FF79C6">        @media</span><span style="color:#F8F8F2"> (</span><span style="color:#8BE9FD">min-width</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> $breakpoint-extra-large) {</span></span>
<span class="line"><span style="color:#FF79C6">            @content</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">@else </span><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#FF79C6">        @error</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">UNKNOWN MEDIA BREAKPOINT </span><span style="color:#FF79C6">#{</span><span style="color:#F8F8F2">$breakpoint</span><span style="color:#FF79C6">}</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">@mixin</span><span style="color:#50FA7B"> light-theme</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    :</span><span style="color:#8BE9FD">global</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">html[theme=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">light</span><span style="color:#E9F284">"</span><span style="color:#FFB86C;font-style:italic">]</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">        @</span><span style="color:#BD93F9">content</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">@mixin</span><span style="color:#50FA7B"> dark-theme</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    :</span><span style="color:#8BE9FD">global</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">html[theme=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">dark</span><span style="color:#E9F284">"</span><span style="color:#FFB86C;font-style:italic">]</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">        @</span><span style="color:#BD93F9">content</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><blockquote>
<p>As I’ve already shared and explained the snippet above in a previous blog post, I won’t go into extensive detail here. If you’re interested in learning more, please refer to my article titled “<a href="https://medium.com/geekculture/sass-media-queries-mixins-1c5e5f605704" rel="noopener noreferrer external">Sass Media Queries Mixins</a>”.</p>
</blockquote>
<hr />
<h2>Class Generator</h2>
<p>Finally, we’ve reached the heart of our solution. To produce the responsive classes we’ve discussed, I’ve created the following Sass mixin in a file I’ve named <code>_utilities.scss</code>:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">@use</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">media</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">@mixin</span><span style="color:#50FA7B"> generate</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">$selector</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">$property</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">$value</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    .xs</span><span style="color:#FF79C6;font-style:italic">\:#{</span><span style="color:#F8F8F2;font-style:italic">$selector</span><span style="color:#FF79C6;font-style:italic">}</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        @include</span><span style="color:#F8F8F2"> media.</span><span style="color:#50FA7B">min-width</span><span style="color:#F8F8F2">(xsmall) {</span></span>
<span class="line"><span style="color:#FF79C6">            #{</span><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">}:</span><span style="color:#F8F8F2"> $value;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    .sm</span><span style="color:#FF79C6;font-style:italic">\:#{</span><span style="color:#F8F8F2;font-style:italic">$selector</span><span style="color:#FF79C6;font-style:italic">}</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    .sm</span><span style="color:#FF79C6;font-style:italic">\:#{</span><span style="color:#F8F8F2;font-style:italic">$selector</span><span style="color:#FF79C6;font-style:italic">}</span><span style="color:#F8F8F2">[</span><span style="color:#50FA7B;font-style:italic">class</span><span style="color:#FF79C6">*=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">-</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    .sm</span><span style="color:#FF79C6;font-style:italic">\:#{</span><span style="color:#F8F8F2;font-style:italic">$selector</span><span style="color:#FF79C6;font-style:italic">}</span><span style="color:#F8F8F2">[</span><span style="color:#50FA7B;font-style:italic">class</span><span style="color:#FF79C6">*=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">xs</span><span style="color:#FF79C6">\:</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">] {</span></span>
<span class="line"><span style="color:#FF79C6">        @include</span><span style="color:#F8F8F2"> media.</span><span style="color:#50FA7B">min-width</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">small</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">            #{</span><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">}:</span><span style="color:#F8F8F2"> $value;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    .md</span><span style="color:#FF79C6;font-style:italic">\:#{</span><span style="color:#F8F8F2;font-style:italic">$selector</span><span style="color:#FF79C6;font-style:italic">}</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    .md</span><span style="color:#FF79C6;font-style:italic">\:#{</span><span style="color:#F8F8F2;font-style:italic">$selector</span><span style="color:#FF79C6;font-style:italic">}</span><span style="color:#F8F8F2">[</span><span style="color:#50FA7B;font-style:italic">class</span><span style="color:#FF79C6">*=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">-</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    .md</span><span style="color:#FF79C6;font-style:italic">\:#{</span><span style="color:#F8F8F2;font-style:italic">$selector</span><span style="color:#FF79C6;font-style:italic">}</span><span style="color:#F8F8F2">[</span><span style="color:#50FA7B;font-style:italic">class</span><span style="color:#FF79C6">*=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">xs</span><span style="color:#FF79C6">\:</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    .md</span><span style="color:#FF79C6;font-style:italic">\:#{</span><span style="color:#F8F8F2;font-style:italic">$selector</span><span style="color:#FF79C6;font-style:italic">}</span><span style="color:#F8F8F2">[</span><span style="color:#50FA7B;font-style:italic">class</span><span style="color:#FF79C6">*=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">sm</span><span style="color:#FF79C6">\:</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">] {</span></span>
<span class="line"><span style="color:#FF79C6">        @include</span><span style="color:#F8F8F2"> media.</span><span style="color:#50FA7B">min-width</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">medium</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">            #{</span><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">}:</span><span style="color:#F8F8F2"> $value;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    .lg</span><span style="color:#FF79C6;font-style:italic">\:#{</span><span style="color:#F8F8F2;font-style:italic">$selector</span><span style="color:#FF79C6;font-style:italic">}</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    .lg</span><span style="color:#FF79C6;font-style:italic">\:#{</span><span style="color:#F8F8F2;font-style:italic">$selector</span><span style="color:#FF79C6;font-style:italic">}</span><span style="color:#F8F8F2">[</span><span style="color:#50FA7B;font-style:italic">class</span><span style="color:#FF79C6">*=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">-</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    .lg</span><span style="color:#FF79C6;font-style:italic">\:#{</span><span style="color:#F8F8F2;font-style:italic">$selector</span><span style="color:#FF79C6;font-style:italic">}</span><span style="color:#F8F8F2">[</span><span style="color:#50FA7B;font-style:italic">class</span><span style="color:#FF79C6">*=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">xs</span><span style="color:#FF79C6">\:</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    .lg</span><span style="color:#FF79C6;font-style:italic">\:#{</span><span style="color:#F8F8F2;font-style:italic">$selector</span><span style="color:#FF79C6;font-style:italic">}</span><span style="color:#F8F8F2">[</span><span style="color:#50FA7B;font-style:italic">class</span><span style="color:#FF79C6">*=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">sm</span><span style="color:#FF79C6">\:</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    .lg</span><span style="color:#FF79C6;font-style:italic">\:#{</span><span style="color:#F8F8F2;font-style:italic">$selector</span><span style="color:#FF79C6;font-style:italic">}</span><span style="color:#F8F8F2">[</span><span style="color:#50FA7B;font-style:italic">class</span><span style="color:#FF79C6">*=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">md</span><span style="color:#FF79C6">\:</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">] {</span></span>
<span class="line"><span style="color:#FF79C6">        @include</span><span style="color:#F8F8F2"> media.</span><span style="color:#50FA7B">min-width</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">large</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">            #{</span><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">}:</span><span style="color:#F8F8F2"> $value;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    .xl</span><span style="color:#FF79C6;font-style:italic">\:#{</span><span style="color:#F8F8F2;font-style:italic">$selector</span><span style="color:#FF79C6;font-style:italic">}</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    .xl</span><span style="color:#FF79C6;font-style:italic">\:#{</span><span style="color:#F8F8F2;font-style:italic">$selector</span><span style="color:#FF79C6;font-style:italic">}</span><span style="color:#F8F8F2">[</span><span style="color:#50FA7B;font-style:italic">class</span><span style="color:#FF79C6">*=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">-</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    .xl</span><span style="color:#FF79C6;font-style:italic">\:#{</span><span style="color:#F8F8F2;font-style:italic">$selector</span><span style="color:#FF79C6;font-style:italic">}</span><span style="color:#F8F8F2">[</span><span style="color:#50FA7B;font-style:italic">class</span><span style="color:#FF79C6">*=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">xs</span><span style="color:#FF79C6">\:</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    .xl</span><span style="color:#FF79C6;font-style:italic">\:#{</span><span style="color:#F8F8F2;font-style:italic">$selector</span><span style="color:#FF79C6;font-style:italic">}</span><span style="color:#F8F8F2">[</span><span style="color:#50FA7B;font-style:italic">class</span><span style="color:#FF79C6">*=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">sm</span><span style="color:#FF79C6">\:</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    .xl</span><span style="color:#FF79C6;font-style:italic">\:#{</span><span style="color:#F8F8F2;font-style:italic">$selector</span><span style="color:#FF79C6;font-style:italic">}</span><span style="color:#F8F8F2">[</span><span style="color:#50FA7B;font-style:italic">class</span><span style="color:#FF79C6">*=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">md</span><span style="color:#FF79C6">\:</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    .xl</span><span style="color:#FF79C6;font-style:italic">\:#{</span><span style="color:#F8F8F2;font-style:italic">$selector</span><span style="color:#FF79C6;font-style:italic">}</span><span style="color:#F8F8F2">[</span><span style="color:#50FA7B;font-style:italic">class</span><span style="color:#FF79C6">*=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">lg</span><span style="color:#FF79C6">\:</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">] {</span></span>
<span class="line"><span style="color:#FF79C6">        @include</span><span style="color:#F8F8F2"> media.</span><span style="color:#50FA7B">min-width</span><span style="color:#F8F8F2">(xlarge) {</span></span>
<span class="line"><span style="color:#FF79C6">            #{</span><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">}:</span><span style="color:#F8F8F2"> $value;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    .</span><span style="color:#FF79C6;font-style:italic">#{</span><span style="color:#F8F8F2;font-style:italic">$selector</span><span style="color:#FF79C6;font-style:italic">}</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        #{</span><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">}:</span><span style="color:#F8F8F2"> $value;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>This utility accepts three parameters:</p>
<ol>
<li><p><code>$selector</code>: The classname which can ultimately be used in the components.</p></li>
<li><p><code>$property</code>: The CSS property that needs to be set, such as <code>display</code> or <code>font-weight</code>.</p></li>
<li><p><code>$value</code>: The expected value of the style, for example, <code>block</code> or <code>700</code> in the previous examples.</p></li>
</ol>
<p>For each combination of parameters, the mixin generates CSS classes defined globally, including their corresponding responsive representations. These classes are meticulously organized in a specific order, beginning with the smallest media query and concluding with the wider ones, all followed by the global value.</p>
<p>For example, when using the generator with a selector called <code>block</code> and specifying <code>display</code> as the property and <code>block</code> as the value, it results in the following generated code:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">/**</span></span>
<span class="line"><span style="color:#6272A4"> * Example of output; do not copy this in your app.</span></span>
<span class="line"><span style="color:#6272A4"> */</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">@media</span><span style="color:#F8F8F2"> (</span><span style="color:#8BE9FD">min-width</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 320</span><span style="color:#FF79C6">px</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    .xs</span><span style="color:#FF79C6;font-style:italic">\:</span><span style="color:#50FA7B;font-style:italic">block</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD">        display</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> block</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">@media</span><span style="color:#F8F8F2"> (</span><span style="color:#8BE9FD">min-width</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 576</span><span style="color:#FF79C6">px</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    .sm</span><span style="color:#FF79C6;font-style:italic">\:</span><span style="color:#50FA7B;font-style:italic">block</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    .sm</span><span style="color:#FF79C6;font-style:italic">\:</span><span style="color:#50FA7B;font-style:italic">block</span><span style="color:#F8F8F2">[</span><span style="color:#50FA7B;font-style:italic">class</span><span style="color:#FF79C6">*=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">-</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    .sm</span><span style="color:#FF79C6;font-style:italic">\:</span><span style="color:#50FA7B;font-style:italic">block</span><span style="color:#F8F8F2">[</span><span style="color:#50FA7B;font-style:italic">class</span><span style="color:#FF79C6">*=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">xs:</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">] {</span></span>
<span class="line"><span style="color:#8BE9FD">        display</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> block</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">@media</span><span style="color:#F8F8F2"> (</span><span style="color:#8BE9FD">min-width</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 768</span><span style="color:#FF79C6">px</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    .md</span><span style="color:#FF79C6;font-style:italic">\:</span><span style="color:#50FA7B;font-style:italic">block</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    .md</span><span style="color:#FF79C6;font-style:italic">\:</span><span style="color:#50FA7B;font-style:italic">block</span><span style="color:#F8F8F2">[</span><span style="color:#50FA7B;font-style:italic">class</span><span style="color:#FF79C6">*=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">-</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    .md</span><span style="color:#FF79C6;font-style:italic">\:</span><span style="color:#50FA7B;font-style:italic">block</span><span style="color:#F8F8F2">[</span><span style="color:#50FA7B;font-style:italic">class</span><span style="color:#FF79C6">*=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">xs:</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    .md</span><span style="color:#FF79C6;font-style:italic">\:</span><span style="color:#50FA7B;font-style:italic">block</span><span style="color:#F8F8F2">[</span><span style="color:#50FA7B;font-style:italic">class</span><span style="color:#FF79C6">*=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">sm:</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">] {</span></span>
<span class="line"><span style="color:#8BE9FD">        display</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> block</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">@media</span><span style="color:#F8F8F2"> (</span><span style="color:#8BE9FD">min-width</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 1024</span><span style="color:#FF79C6">px</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    .lg</span><span style="color:#FF79C6;font-style:italic">\:</span><span style="color:#50FA7B;font-style:italic">block</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    .lb</span><span style="color:#FF79C6;font-style:italic">\:</span><span style="color:#50FA7B;font-style:italic">block</span><span style="color:#F8F8F2">[</span><span style="color:#50FA7B;font-style:italic">class</span><span style="color:#FF79C6">*=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">-</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    .lg</span><span style="color:#FF79C6;font-style:italic">\:</span><span style="color:#50FA7B;font-style:italic">block</span><span style="color:#F8F8F2">[</span><span style="color:#50FA7B;font-style:italic">class</span><span style="color:#FF79C6">*=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">xs:</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    .lg</span><span style="color:#FF79C6;font-style:italic">\:</span><span style="color:#50FA7B;font-style:italic">block</span><span style="color:#F8F8F2">[</span><span style="color:#50FA7B;font-style:italic">class</span><span style="color:#FF79C6">*=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">sm:</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    .lg</span><span style="color:#FF79C6;font-style:italic">\:</span><span style="color:#50FA7B;font-style:italic">block</span><span style="color:#F8F8F2">[</span><span style="color:#50FA7B;font-style:italic">class</span><span style="color:#FF79C6">*=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">md:</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">] {</span></span>
<span class="line"><span style="color:#8BE9FD">        display</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> block</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">@media</span><span style="color:#F8F8F2"> (</span><span style="color:#8BE9FD">min-width</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 1300</span><span style="color:#FF79C6">px</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    .xl</span><span style="color:#FF79C6;font-style:italic">\:</span><span style="color:#50FA7B;font-style:italic">block</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    .xl</span><span style="color:#FF79C6;font-style:italic">\:</span><span style="color:#50FA7B;font-style:italic">block</span><span style="color:#F8F8F2">[</span><span style="color:#50FA7B;font-style:italic">class</span><span style="color:#FF79C6">*=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">-</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    .xl</span><span style="color:#FF79C6;font-style:italic">\:</span><span style="color:#50FA7B;font-style:italic">block</span><span style="color:#F8F8F2">[</span><span style="color:#50FA7B;font-style:italic">class</span><span style="color:#FF79C6">*=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">xs:</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    .xl</span><span style="color:#FF79C6;font-style:italic">\:</span><span style="color:#50FA7B;font-style:italic">block</span><span style="color:#F8F8F2">[</span><span style="color:#50FA7B;font-style:italic">class</span><span style="color:#FF79C6">*=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">sm:</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    .xl</span><span style="color:#FF79C6;font-style:italic">\:</span><span style="color:#50FA7B;font-style:italic">block</span><span style="color:#F8F8F2">[</span><span style="color:#50FA7B;font-style:italic">class</span><span style="color:#FF79C6">*=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">md:</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    .xl</span><span style="color:#FF79C6;font-style:italic">\:</span><span style="color:#50FA7B;font-style:italic">block</span><span style="color:#F8F8F2">[</span><span style="color:#50FA7B;font-style:italic">class</span><span style="color:#FF79C6">*=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">lg:</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">] {</span></span>
<span class="line"><span style="color:#8BE9FD">        display</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> block</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">.block</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD">    display</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> block</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h2>Usage</h2>
<p>The generator mixin handles the task of class generation, but it doesn’t have knowledge of which classes need to be generated. Therefore, when integrating this solution, developers are still responsible for declaring the specific classes they intend to use in their application.</p>
<p>I’ve found it beneficial to separate different styles into dedicated Sass files, such as creating a dedicated Sass file for each style topic.</p>
<p>To demonstrate this usage, I’ll share the classes I’ve declared, which are utilized in the code snippet showcased in the first chapter of this tutorial.</p>
<p><code>display.scss</code> :</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">@use</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">../mixins/utilities</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD"> display</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">hidden</span><span style="color:#F8F8F2">, $property, </span><span style="color:#BD93F9">none</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">block</span><span style="color:#F8F8F2">, $property, </span><span style="color:#BD93F9">block</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">flex</span><span style="color:#F8F8F2">, $property, </span><span style="color:#BD93F9">flex</span><span style="color:#F8F8F2">);</span></span></code></pre><p><code>justify-content.scss</code> :</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">@use</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">../mixins/utilities</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD"> justify-content</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(justify</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">start, $property, </span><span style="color:#BD93F9">flex-start</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(justify</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">end, $property, </span><span style="color:#BD93F9">flex-end</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(justify</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">center, $property, </span><span style="color:#BD93F9">center</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(justify</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">between, $property, </span><span style="color:#BD93F9">space-between</span><span style="color:#F8F8F2">);</span></span></code></pre><p><code>gap.scss</code> :</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">@use</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">../mixins/utilities</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD"> gap</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(gap</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">1, $property, </span><span style="color:#8BE9FD">var</span><span style="color:#F8F8F2">(--padding));</span></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(gap</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">2, </span><span style="color:#FF79C6">#{</span><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD">var</span><span style="color:#F8F8F2">(--padding-2x));</span></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(gap</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">4, </span><span style="color:#FF79C6">#{</span><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD">var</span><span style="color:#F8F8F2">(--padding-4x));</span></span></code></pre><blockquote>
<p>Note that in the application, I make use of global CSS variables, such as --padding set to 8px.</p>
</blockquote>
<p><code>margin.scss</code> :</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">@use</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">../mixins/utilities</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD"> margin</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(m</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">0, $property, </span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(mx</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">0, </span><span style="color:#FF79C6">#{</span><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">}-</span><span style="color:#F8F8F2">left, </span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(mx</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">0, </span><span style="color:#FF79C6">#{</span><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">}-</span><span style="color:#F8F8F2">right, </span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(ml</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">0, </span><span style="color:#FF79C6">#{</span><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">}-</span><span style="color:#F8F8F2">left, </span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(mr</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">0, </span><span style="color:#FF79C6">#{</span><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">}-</span><span style="color:#F8F8F2">right, </span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(my</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">0, </span><span style="color:#FF79C6">#{</span><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">}-</span><span style="color:#F8F8F2">top, </span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(my</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">0, </span><span style="color:#FF79C6">#{</span><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">}-</span><span style="color:#F8F8F2">bottom, </span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(mt</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">0, </span><span style="color:#FF79C6">#{</span><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">}-</span><span style="color:#F8F8F2">top, </span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(mb</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">0, </span><span style="color:#FF79C6">#{</span><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">}-</span><span style="color:#F8F8F2">bottom, </span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(m</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">1, $property, </span><span style="color:#8BE9FD">var</span><span style="color:#F8F8F2">(--padding));</span></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(mx</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">1, </span><span style="color:#FF79C6">#{</span><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">}-</span><span style="color:#F8F8F2">left, </span><span style="color:#8BE9FD">var</span><span style="color:#F8F8F2">(--padding));</span></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(mx</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">1, </span><span style="color:#FF79C6">#{</span><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">}-</span><span style="color:#F8F8F2">right, </span><span style="color:#8BE9FD">var</span><span style="color:#F8F8F2">(--padding));</span></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(ml</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">1, </span><span style="color:#FF79C6">#{</span><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">}-</span><span style="color:#F8F8F2">left, </span><span style="color:#8BE9FD">var</span><span style="color:#F8F8F2">(--padding));</span></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(mr</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">1, </span><span style="color:#FF79C6">#{</span><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">}-</span><span style="color:#F8F8F2">right, </span><span style="color:#8BE9FD">var</span><span style="color:#F8F8F2">(--padding));</span></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(my</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">1, </span><span style="color:#FF79C6">#{</span><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">}-</span><span style="color:#F8F8F2">top, </span><span style="color:#8BE9FD">var</span><span style="color:#F8F8F2">(--padding));</span></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(my</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">1, </span><span style="color:#FF79C6">#{</span><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">}-</span><span style="color:#F8F8F2">bottom, </span><span style="color:#8BE9FD">var</span><span style="color:#F8F8F2">(--padding));</span></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(mt</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">1, </span><span style="color:#FF79C6">#{</span><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">}-</span><span style="color:#F8F8F2">top, </span><span style="color:#8BE9FD">var</span><span style="color:#F8F8F2">(--padding));</span></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(mb</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">1, </span><span style="color:#FF79C6">#{</span><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">}-</span><span style="color:#F8F8F2">bottom, </span><span style="color:#8BE9FD">var</span><span style="color:#F8F8F2">(--padding));</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(m</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">2, $property, </span><span style="color:#8BE9FD">var</span><span style="color:#F8F8F2">(--padding-2x));</span></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(mx</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">2, </span><span style="color:#FF79C6">#{</span><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">}-</span><span style="color:#F8F8F2">left, </span><span style="color:#8BE9FD">var</span><span style="color:#F8F8F2">(--padding-2x));</span></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(mx</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">2, </span><span style="color:#FF79C6">#{</span><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">}-</span><span style="color:#F8F8F2">right, </span><span style="color:#8BE9FD">var</span><span style="color:#F8F8F2">(--padding-2x));</span></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(ml</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">2, </span><span style="color:#FF79C6">#{</span><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">}-</span><span style="color:#F8F8F2">left, </span><span style="color:#8BE9FD">var</span><span style="color:#F8F8F2">(--padding-2x));</span></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(mr</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">2, </span><span style="color:#FF79C6">#{</span><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">}-</span><span style="color:#F8F8F2">right, </span><span style="color:#8BE9FD">var</span><span style="color:#F8F8F2">(--padding-2x));</span></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(my</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">2, </span><span style="color:#FF79C6">#{</span><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">}-</span><span style="color:#F8F8F2">top, </span><span style="color:#8BE9FD">var</span><span style="color:#F8F8F2">(--padding-2x));</span></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(my</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">2, </span><span style="color:#FF79C6">#{</span><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">}-</span><span style="color:#F8F8F2">bottom, </span><span style="color:#8BE9FD">var</span><span style="color:#F8F8F2">(--padding-2x));</span></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(mt</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">2, </span><span style="color:#FF79C6">#{</span><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">}-</span><span style="color:#F8F8F2">top, </span><span style="color:#8BE9FD">var</span><span style="color:#F8F8F2">(--padding-2x));</span></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(mb</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">2, </span><span style="color:#FF79C6">#{</span><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">}-</span><span style="color:#F8F8F2">bottom, </span><span style="color:#8BE9FD">var</span><span style="color:#F8F8F2">(--padding-2x));</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(m</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">4, $property, </span><span style="color:#8BE9FD">var</span><span style="color:#F8F8F2">(--padding-4x));</span></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(mx</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">4, </span><span style="color:#FF79C6">#{</span><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">}-</span><span style="color:#F8F8F2">left, </span><span style="color:#8BE9FD">var</span><span style="color:#F8F8F2">(--padding-4x));</span></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(mx</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">4, </span><span style="color:#FF79C6">#{</span><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">}-</span><span style="color:#F8F8F2">right, </span><span style="color:#8BE9FD">var</span><span style="color:#F8F8F2">(--padding-4x));</span></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(ml</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">4, </span><span style="color:#FF79C6">#{</span><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">}-</span><span style="color:#F8F8F2">left, </span><span style="color:#8BE9FD">var</span><span style="color:#F8F8F2">(--padding-4x));</span></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(mr</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">4, </span><span style="color:#FF79C6">#{</span><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">}-</span><span style="color:#F8F8F2">right, </span><span style="color:#8BE9FD">var</span><span style="color:#F8F8F2">(--padding-4x));</span></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(my</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">4, </span><span style="color:#FF79C6">#{</span><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">}-</span><span style="color:#F8F8F2">top, </span><span style="color:#8BE9FD">var</span><span style="color:#F8F8F2">(--padding-4x));</span></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(my</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">4, </span><span style="color:#FF79C6">#{</span><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">}-</span><span style="color:#F8F8F2">bottom, </span><span style="color:#8BE9FD">var</span><span style="color:#F8F8F2">(--padding-4x));</span></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(mt</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">4, </span><span style="color:#FF79C6">#{</span><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">}-</span><span style="color:#F8F8F2">top, </span><span style="color:#8BE9FD">var</span><span style="color:#F8F8F2">(--padding-4x));</span></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(mb</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">4, </span><span style="color:#FF79C6">#{</span><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">}-</span><span style="color:#F8F8F2">bottom, </span><span style="color:#8BE9FD">var</span><span style="color:#F8F8F2">(--padding-4x));</span></span></code></pre><p><code>color.scss</code> :</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">@use</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">../mixins/utilities</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> color</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(text</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">blue, $property, </span><span style="color:#BD93F9">#3b00b9</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(text</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">cyclamen, $property, </span><span style="color:#BD93F9">#ea6c99</span><span style="color:#F8F8F2">);</span></span></code></pre><p><code>font-weight.scss</code> :</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">@use</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">../mixins/utilities</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">$property</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD"> font-weight</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(font</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">normal, $property, </span><span style="color:#BD93F9">400</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">@include</span><span style="color:#F8F8F2"> utilities.</span><span style="color:#50FA7B">generate</span><span style="color:#F8F8F2">(font</span><span style="color:#BD93F9">-</span><span style="color:#F8F8F2">bold, $property, </span><span style="color:#BD93F9">700</span><span style="color:#F8F8F2">);</span></span></code></pre><p>As you can see, it’s quite straightforward to generate these class helpers using the mixin we’ve created above. This approach not only simplifies class declarations but also reduces them to just a few lines of code.</p>
<blockquote>
<p>For more examples of usage, you can explore the Oisy’s <a href="https://github.com/dfinity/oisy-wallet/tree/main/src/frontend/src/lib/styles/utilities" rel="noopener noreferrer external">repository</a>.</p>
</blockquote>
<hr />
<h2>Conclusion</h2>
<p>In closing, we’ve explored the creation of a dynamic and responsive class generation solution using Sass mixins. This approach has fulfilled my initial intent and exploration. While I may not be certain about reusing it in upcoming projects, there are certainly several valuable concepts within it that I believe are worth revisiting in the future.</p>
<p>I must admit, as a developer who generally isn’t particularly fond of CSS class-based approaches, I not only enjoyed developing it but have also found a certain affinity for using it.</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[JSON Parse And Stringify: BigInt, Objects and Uint8Arrays]]></title>
          <description><![CDATA[Learn JSON Handling for BigInts, Objects, and Uint8Arrays with Replacer and Reviver. Discover parsing, stringify, and testing strategies.]]></description>
          <link>https://daviddalbusco.com/blog/json-parse-and-stringify-bigint-objects-and-uint8arrays/</link>
          <pubDate>Sun, 20 Aug 2023 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*V4hY8EjtJ0jlbJw4l9R4tg.jpeg" alt="" / loading="lazy"></p>
<p>Photo by <a href="https://unsplash.com/fr/@aronvisuals?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Aron Visuals</a> on <a href="https://unsplash.com/fr/photos/bZZp1PmHI0E?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></p>
<hr />
<p>As I’ve been doing in various recent projects, you might find yourself needing to <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify" rel="noopener noreferrer external">stringify</a> and <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse" rel="noopener noreferrer external">parse</a> bigints, objects, or Uint8Arrays, which aren’t supported natively by these two functions.</p>
<p>Before exploring the solution, let’s identify the errors you might have come across, prompting you to read this article.</p>
<h2>BigInt</h2>
<p>JSON finds widespread usage across various systems, and from my observation, introducing support for new types could potentially result in disruptive changes. This is why BigInts were probably omitted from the ECMAScript specification for <a href="https://tc39.es/ecma262/#sec-serializejsonproperty" rel="noopener noreferrer external">SerializeJSONProperty</a>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> value </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> 123456</span><span style="color:#FF79C6">n</span></span>
<span class="line"><span style="color:#BD93F9">JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">stringify</span><span style="color:#F8F8F2">(value)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">>></span><span style="color:#F8F8F2"> Uncaught TypeError: BigInt value can</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">t be serialized in JSO</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">N</span></span></code></pre><h2>Objects</h2>
<p>While converting and parsing objects doesn’t inherently throw an error, stringifying and parsing will result in the loss of the objects’ functions. This can be inconvenient if your goal is to maintain the nature of the utilized class.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">class</span><span style="color:#8BE9FD"> Dog</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">  woof</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#F8F8F2">        console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">woof</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> dog </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Dog</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">dog.</span><span style="color:#50FA7B">woof</span><span style="color:#F8F8F2">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">>></span><span style="color:#F8F8F2"> woof</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> str </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">stringify</span><span style="color:#F8F8F2">(dog)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> result </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">parse</span><span style="color:#F8F8F2">(str)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">result.</span><span style="color:#50FA7B">woof</span><span style="color:#F8F8F2">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">>></span><span style="color:#F8F8F2"> Uncaught TypeError: result.woof is not a </span><span style="color:#FF79C6">function</span></span></code></pre><h2>Uint8Array</h2>
<p>Encoding and decoding Uint8Array doesn’t throw errors either, but it also doesn’t recreate the expected types. Upon parsing, the result turns into an object instead of the anticipated array.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> value </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> Uint8Array.</span><span style="color:#50FA7B">from</span><span style="color:#F8F8F2">([</span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">2</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">3</span><span style="color:#F8F8F2">])</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(value)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">>></span><span style="color:#50FA7B"> Uint8Array</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">3</span><span style="color:#F8F8F2">) [ </span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">2</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">3</span><span style="color:#F8F8F2"> ]</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> str </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">stringify</span><span style="color:#F8F8F2">(value)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> result </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">parse</span><span style="color:#F8F8F2">(str)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(result)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">>></span><span style="color:#F8F8F2"> Object { </span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">2</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">2</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">3</span><span style="color:#F8F8F2"> }</span></span></code></pre><h2>Approach</h2>
<p>Dealing with serialization of BigInts can be addressed by implementing a solution known as <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#description" rel="noopener noreferrer external">monkey patching</a> (<code>BigInt.prototype.toJSON = ...</code>) to incorporate a <code>toJSON()</code> method within the type.</p>
<p>However, our approach in this solution differs, as we aim to address additional requirements and prefer to tackle all challenges using a unified method.</p>
<p>The solution involves utilizing the optional <code>replacer</code> and <code>reviver</code> functions of the JSON API to modify the behavior of the stringification and parsing processes.</p>
<p>The concept is to intercept the types we intend to enhance support for, and manually convert them into intermediary values that allow us to maintain the essence of the type while storing their values in objects distinguished by custom constants.</p>
<p>For instance, during the stringify process, when encountering a bigint, we transform it into an object such as <code>{ __bigint__: ‘123456’ }</code>.</p>
<p>Subsequently, during the parsing of the result, we revert these objects back to bigints if we identify them by their key, like so: if <code>typeof value === &quot;object&quot;</code> and <code>'__bigint__'</code> exists in <code>value</code>, then return <code>BigInt(value.__bigint__)</code>.</p>
<p>However, this approach comes with a caveat: the constant identifiers we employ to indicate the manually encoded and decoded types must not serve any other purpose within the objects. Otherwise, these identifiers would be subject to manual decoding. For instance, if an object were to include <code>{ __bigint__: 123456 }</code>, the parsed value would not be a number but a bigint. Hence, I recommend utilizing highly distinctive keys and, if required, implementing additional checks within the application.</p>
<h2>Solution</h2>
<p>Long story short, here’s the parse I use to stringify and parse BigInts, particulary objects and Uint8Array:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> JSON_KEY_BIGINT </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">__bigint__</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> JSON_KEY_MyObject </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">__MyObject__</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> JSON_KEY_UINT8ARRAY </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">__uint8array__</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// An inlined utilise to check for null and undefined</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> nonNullish</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">>(</span><span style="color:#FFB86C;font-style:italic">argument</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> T</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#FFB86C;font-style:italic"> argument</span><span style="color:#FF79C6"> is</span><span style="color:#8BE9FD;font-style:italic"> NonNullable</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=></span></span>
<span class="line"><span style="color:#F8F8F2">    argument </span><span style="color:#FF79C6">!==</span><span style="color:#BD93F9"> null</span><span style="color:#FF79C6"> &#x26;&#x26;</span><span style="color:#F8F8F2"> argument </span><span style="color:#FF79C6">!==</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// An object for showcase purpose</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> MyObject</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    constructor</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">public</span><span style="color:#FFB86C;font-style:italic"> value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">) {}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    toText</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.value;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    static</span><span style="color:#50FA7B"> fromText</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> MyObject</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#6272A4">        // This is used to recreate the object from its textual representation.</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> MyObject</span><span style="color:#F8F8F2">(value);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// The parser that interprets revived BigInt, MyObject, and Uint8Array when constructing JavaScript values or objects.</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> jsonReplacer</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">_key</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> unknown</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> unknown</span><span style="color:#FF79C6"> =></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">typeof</span><span style="color:#F8F8F2"> value </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">bigint</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> { [JSON_KEY_BIGINT]</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> `</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">value</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2"> };</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#50FA7B">nonNullish</span><span style="color:#F8F8F2">(value) </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> value </span><span style="color:#FF79C6">instanceof</span><span style="color:#8BE9FD;font-style:italic"> MyObject</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> { [JSON_KEY_MyObject]</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> value.</span><span style="color:#50FA7B">toText</span><span style="color:#F8F8F2">() };</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#50FA7B">nonNullish</span><span style="color:#F8F8F2">(value) </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> value </span><span style="color:#FF79C6">instanceof</span><span style="color:#8BE9FD;font-style:italic"> Uint8Array</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> { [JSON_KEY_UINT8ARRAY]</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> Array.</span><span style="color:#50FA7B">from</span><span style="color:#F8F8F2">(value) };</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> value;</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// A function that alters the behavior of the stringification process for BigInt, MyObject and Uint8Array.</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> jsonReviver</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">_key</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> unknown</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> unknown</span><span style="color:#FF79C6"> =></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#50FA7B"> mapValue</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">>(</span><span style="color:#FFB86C;font-style:italic">key</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> T</span><span style="color:#FF79C6"> =></span><span style="color:#F8F8F2"> (value </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> Record</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">>)[key];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#50FA7B">nonNullish</span><span style="color:#F8F8F2">(value) </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#FF79C6"> typeof</span><span style="color:#F8F8F2"> value </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">object</span><span style="color:#E9F284">"</span><span style="color:#FF79C6"> &#x26;&#x26;</span><span style="color:#F8F8F2"> JSON_KEY_BIGINT </span><span style="color:#FF79C6">in</span><span style="color:#F8F8F2"> value) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#50FA7B"> BigInt</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">mapValue</span><span style="color:#F8F8F2">(JSON_KEY_BIGINT));</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#50FA7B">nonNullish</span><span style="color:#F8F8F2">(value) </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#FF79C6"> typeof</span><span style="color:#F8F8F2"> value </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">object</span><span style="color:#E9F284">"</span><span style="color:#FF79C6"> &#x26;&#x26;</span><span style="color:#F8F8F2"> JSON_KEY_MyObject </span><span style="color:#FF79C6">in</span><span style="color:#F8F8F2"> value) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> MyObject.</span><span style="color:#50FA7B">fromText</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">mapValue</span><span style="color:#F8F8F2">(JSON_KEY_MyObject));</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#50FA7B">nonNullish</span><span style="color:#F8F8F2">(value) </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#FF79C6"> typeof</span><span style="color:#F8F8F2"> value </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">object</span><span style="color:#E9F284">"</span><span style="color:#FF79C6"> &#x26;&#x26;</span><span style="color:#F8F8F2"> JSON_KEY_UINT8ARRAY </span><span style="color:#FF79C6">in</span><span style="color:#F8F8F2"> value) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> Uint8Array.</span><span style="color:#50FA7B">from</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">mapValue</span><span style="color:#F8F8F2">(JSON_KEY_UINT8ARRAY));</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> value;</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>The <code>jsonReplacer</code> and <code>jsonReviver</code> can now be integrated into your application's code for the purpose of stringifying and replacing your intricate objects.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> str </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">stringify</span><span style="color:#F8F8F2">(yourObject, jsonReplacer);</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> result </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">parse</span><span style="color:#F8F8F2">(str, jsonReviver);</span></span></code></pre><h2>Tests</h2>
<p>For additional examples and to maintain the solution, presented below are a few tests developed using Jest:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { jsonReplacer, jsonReviver, MyObject } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./json.utils</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">describe</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">json-utils</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    describe</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">stringify</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">        it</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">should stringify bigint with a custom representation</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">            expect</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">stringify</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">123</span><span style="color:#FF79C6">n</span><span style="color:#F8F8F2">, jsonReplacer)).</span><span style="color:#50FA7B">toEqual</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">{"__bigint__":"123"}</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#50FA7B">            expect</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">stringify</span><span style="color:#F8F8F2">({ value</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 123</span><span style="color:#FF79C6">n</span><span style="color:#F8F8F2"> }, jsonReplacer)).</span><span style="color:#50FA7B">toEqual</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#E9F284">                '</span><span style="color:#F1FA8C">{"value":{"__bigint__":"123"}}</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">            );</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">        it</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">should stringify MyObject with a custom representation</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">            const</span><span style="color:#F8F8F2"> obj </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> MyObject</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">hello</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">            expect</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">stringify</span><span style="color:#F8F8F2">(obj, jsonReplacer)).</span><span style="color:#50FA7B">toEqual</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">{"__MyObject__":"hello"}</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#50FA7B">            expect</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">stringify</span><span style="color:#F8F8F2">({ obj }, jsonReplacer)).</span><span style="color:#50FA7B">toEqual</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">{"obj":{"__MyObject__":"hello"}}</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">        it</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">should stringify Uint8Array with a custom representation</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">            const</span><span style="color:#F8F8F2"> arr </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> Uint8Array.</span><span style="color:#50FA7B">from</span><span style="color:#F8F8F2">([</span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">2</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">3</span><span style="color:#F8F8F2">]);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">            expect</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">stringify</span><span style="color:#F8F8F2">(arr, jsonReplacer)).</span><span style="color:#50FA7B">toEqual</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">{"__uint8array__":[1,2,3]}</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#50FA7B">            expect</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">stringify</span><span style="color:#F8F8F2">({ arr }, jsonReplacer)).</span><span style="color:#50FA7B">toEqual</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">{"arr":{"__uint8array__":[1,2,3]}}</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    describe</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">parse</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">        it</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">should parse bigint from a custom representation</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">            expect</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">parse</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">{"__bigint__":"123"}</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, jsonReviver)).</span><span style="color:#50FA7B">toEqual</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">123</span><span style="color:#FF79C6">n</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#50FA7B">            expect</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">parse</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">{"value":{"__bigint__":"123"}}</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, jsonReviver)).</span><span style="color:#50FA7B">toEqual</span><span style="color:#F8F8F2">({ value</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 123</span><span style="color:#FF79C6">n</span><span style="color:#F8F8F2"> });</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">        it</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">should parse principal from a custom representation</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">            const</span><span style="color:#F8F8F2"> obj </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> MyObject</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">hello</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">            expect</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">parse</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">{"__MyObject__":"hello"}</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, jsonReviver)).</span><span style="color:#50FA7B">toEqual</span><span style="color:#F8F8F2">(obj);</span></span>
<span class="line"><span style="color:#50FA7B">            expect</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">parse</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">{"obj":{"__MyObject__":"hello"}}</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, jsonReviver)).</span><span style="color:#50FA7B">toEqual</span><span style="color:#F8F8F2">({ obj });</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">        it</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">should parse principal to object</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">            const</span><span style="color:#F8F8F2"> obj </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">parse</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">{"__MyObject__":"tmxop-wyaaa-aaaaa-aaapa-cai"}</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, jsonReviver);</span></span>
<span class="line"><span style="color:#50FA7B">            expect</span><span style="color:#F8F8F2">(obj </span><span style="color:#FF79C6">instanceof</span><span style="color:#8BE9FD;font-style:italic"> MyObject</span><span style="color:#F8F8F2">).</span><span style="color:#50FA7B">toBeTruthy</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#50FA7B">            expect</span><span style="color:#F8F8F2">((obj </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> MyObject</span><span style="color:#F8F8F2">).</span><span style="color:#50FA7B">toText</span><span style="color:#F8F8F2">()).</span><span style="color:#50FA7B">toEqual</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">tmxop-wyaaa-aaaaa-aaapa-cai</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">        it</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">should parse Uint8Array from a custom representation</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">            const</span><span style="color:#F8F8F2"> arr </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> Uint8Array.</span><span style="color:#50FA7B">from</span><span style="color:#F8F8F2">([</span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">2</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">3</span><span style="color:#F8F8F2">]);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">            expect</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">parse</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">{"__uint8array__":[1,2,3]}</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, jsonReviver)).</span><span style="color:#50FA7B">toEqual</span><span style="color:#F8F8F2">(arr);</span></span>
<span class="line"><span style="color:#50FA7B">            expect</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">parse</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">{"arr":{"__uint8array__":[1,2,3]}}</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, jsonReviver)).</span><span style="color:#50FA7B">toEqual</span><span style="color:#F8F8F2">({ arr });</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><p>Thank you for reading! Follow me on <a href="https://twitter.com/daviddalbusco" rel="noopener noreferrer external">Twitter / X</a> for more exciting coding content.</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Ilanz - Rhäzüns]]></title>
          <link>https://daviddalbusco.com/trails/2023/ilanz-rhazuns/</link>
          <pubDate>Sat, 19 Aug 2023 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>A wonderful easy run through the canyon Ruinaulta.</p>
<p>Waypoints:</p>
<ul>
<li>Ilanz</li>
<li>Versam</li>
<li>Rhäzüns</li>
</ul>
<p>The trail starts and ends at a train station.</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Passing an Async Function as a Parameter in Rust]]></title>
          <description><![CDATA[Learn to pass async functions as callbacks in Rust, exploring traits like Fn, FnMut, and FnOnce]]></description>
          <link>https://daviddalbusco.com/blog/passing-an-async-function-as-a-parameter-in-rust/</link>
          <pubDate>Sat, 05 Aug 2023 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*2N4KeE8ZJ5laOIibPW7a4g.jpeg" alt="" / loading="lazy"></p>
<p>Photo by <a href="https://unsplash.com/fr/@crisdinoto?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Cris DiNoto</a> on <a href="https://unsplash.com/fr/photos/z-lh7Mz7wFQ?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></p>
<hr />
<p>While developing canister smart contracts for Juno in Rust, I must admit that I’m not yet prominent at writing code in this programming language. As a matter of fact, I even consider myself a noob. That’s why when I have to use new patterns, it sometimes took me a bit of time to figure out how. This was exactly the case when I was looking to pass an async function as a callback parameter to another function.</p>
<p>As there is no better documentation for my future self than blogging, let me share the solution I discovered during this intriguing process.</p>
<h2>Solution</h2>
<p>My interest in this solution was sparked by the need to refactor my code base and implement a pattern where an existing function would transform into a factory, with certain parts of it becoming dynamic while retaining its asynchronous characteristic.</p>
<p>To schematize this idea in this article, let’s envision two distinct functions: an incrementor and a square function, both accepting the same parameters and returning the same result.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">type</span><span style="color:#8BE9FD;font-style:italic"> MyParam</span><span style="color:#FF79C6"> =</span><span style="color:#8BE9FD;font-style:italic"> u64</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">type</span><span style="color:#8BE9FD;font-style:italic"> MyResult</span><span style="color:#FF79C6"> =</span><span style="color:#8BE9FD;font-style:italic"> u64</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> fn</span><span style="color:#50FA7B"> inc</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#F8F8F2">    value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> MyParam</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> MyResult</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    value </span><span style="color:#FF79C6">+</span><span style="color:#BD93F9"> 1</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> fn</span><span style="color:#50FA7B"> square</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#F8F8F2">    value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> MyParam</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> MyResult</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    value</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">pow</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">2</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Now that the functions share a common trait, we can proceed to implement the function that accepts them as parameters.</p>
<p>To be able to pass a function or closure as a parameter of another function, we need one of the following traits (<a href="https://stackoverflow.com/a/30232500/5404186" rel="noopener noreferrer external">source</a>):</p>
<ul>
<li><p><a href="https://doc.rust-lang.org/std/ops/trait.FnOnce.html" rel="noopener noreferrer external">FnOnce</a> are functions that can be called once</p></li>
<li><p><a href="https://doc.rust-lang.org/std/ops/trait.FnMut.html" rel="noopener noreferrer external">FnMut</a> are functions that can be called if they have &amp;mut access to their environment</p></li>
<li><p><a href="https://doc.rust-lang.org/std/ops/trait.Fn.html" rel="noopener noreferrer external">Fn</a> are functions that can be called if they only have &amp; access to their environment</p></li>
</ul>
<p>Furthermore, due to the asynchronous nature of the function we aim to pass, it should be specified that it returns a <code>Future</code>. A future represents an asynchronous computation obtained by using <code>async</code>.</p>
<p>Finally, while it could be inlined, for readability reasons, I found it convenient to use <code>where</code> constraints.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">use</span><span style="color:#F8F8F2"> std</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">future</span><span style="color:#FF79C6">::</span><span style="color:#8BE9FD;font-style:italic">Future</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> fn</span><span style="color:#50FA7B"> execute</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">F</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">Fut</span><span style="color:#F8F8F2">>(</span></span>
<span class="line"><span style="color:#F8F8F2">    f</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> F</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> MyParam</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> MyResult</span></span>
<span class="line"><span style="color:#FF79C6">where</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">    F</span><span style="color:#FF79C6">:</span><span style="color:#50FA7B"> FnOnce</span><span style="color:#F8F8F2">(</span><span style="color:#8BE9FD;font-style:italic">MyParam</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> Fut</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">    Fut</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Future</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">Output</span><span style="color:#FF79C6"> =</span><span style="color:#8BE9FD;font-style:italic"> MyResult</span><span style="color:#F8F8F2">>,</span></span>
<span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#50FA7B">    f</span><span style="color:#F8F8F2">(value)</span><span style="color:#FF79C6">.await</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>You might find the above pretty obvious if you are slightly more experienced than me in Rust, but believe me, it wasn’t that straightforward to implement such a pattern within an existing codebase because, of course, I brute-forced the implementation in my project instead of trying it first in a sample repo. 🤪</p>
<p>Anyway, with both the parameters and the function that accepts them ready, we can showcase their execution by calling them in a main function, for example.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">#[tokio</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">main]</span></span>
<span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> fn</span><span style="color:#50FA7B"> main</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> value1 </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> execute</span><span style="color:#F8F8F2">(inc, </span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">.await</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> value2 </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> execute</span><span style="color:#F8F8F2">(square, </span><span style="color:#BD93F9">5</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">.await</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    println!</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">"{} {}"</span><span style="color:#F8F8F2">, value1, value2);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><h3>Summary</h3>
<p>In case you would like to run the snippet in the <a href="https://play.rust-lang.org" rel="noopener noreferrer external">Rust Playground</a>, here’s the entire code above, all together:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">use</span><span style="color:#F8F8F2"> std</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">future</span><span style="color:#FF79C6">::</span><span style="color:#8BE9FD;font-style:italic">Future</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">type</span><span style="color:#8BE9FD;font-style:italic"> MyParam</span><span style="color:#FF79C6"> =</span><span style="color:#8BE9FD;font-style:italic"> u64</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">type</span><span style="color:#8BE9FD;font-style:italic"> MyResult</span><span style="color:#FF79C6"> =</span><span style="color:#8BE9FD;font-style:italic"> u64</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> fn</span><span style="color:#50FA7B"> inc</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#F8F8F2">    value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> MyParam</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> MyResult</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    value </span><span style="color:#FF79C6">+</span><span style="color:#BD93F9"> 1</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> fn</span><span style="color:#50FA7B"> square</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#F8F8F2">    value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> MyParam</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> MyResult</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    value</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">pow</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">2</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> fn</span><span style="color:#50FA7B"> execute</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">F</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">Fut</span><span style="color:#F8F8F2">>(</span></span>
<span class="line"><span style="color:#F8F8F2">    f</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> F</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> MyParam</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> MyResult</span></span>
<span class="line"><span style="color:#FF79C6">where</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">    F</span><span style="color:#FF79C6">:</span><span style="color:#50FA7B"> FnOnce</span><span style="color:#F8F8F2">(</span><span style="color:#8BE9FD;font-style:italic">MyParam</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> Fut</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">    Fut</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Future</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">Output</span><span style="color:#FF79C6"> =</span><span style="color:#8BE9FD;font-style:italic"> MyResult</span><span style="color:#F8F8F2">>,</span></span>
<span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#50FA7B">    f</span><span style="color:#F8F8F2">(value)</span><span style="color:#FF79C6">.await</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">#[tokio</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">main]</span></span>
<span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> fn</span><span style="color:#50FA7B"> main</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> value1 </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> execute</span><span style="color:#F8F8F2">(inc, </span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">.await</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> value2 </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> execute</span><span style="color:#F8F8F2">(square, </span><span style="color:#BD93F9">5</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">.await</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    println!</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">"{} {}"</span><span style="color:#F8F8F2">, value1, value2);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><h2>Just passing a function</h2>
<p>You might be looking as well how to pass a synchronous function as parameter. This is also possible. We can adapt the previous solution by removing the <code>async</code> nature and leveraging the <code>dyn</code> keyword to highlight that calls to methods on the associated Trait are dynamically dispatched.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">type</span><span style="color:#8BE9FD;font-style:italic"> MyParam</span><span style="color:#FF79C6"> =</span><span style="color:#8BE9FD;font-style:italic"> u64</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">type</span><span style="color:#8BE9FD;font-style:italic"> MyResult</span><span style="color:#FF79C6"> =</span><span style="color:#8BE9FD;font-style:italic"> u64</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">fn</span><span style="color:#50FA7B"> inc</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#F8F8F2">    value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> MyParam</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> MyResult</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    value </span><span style="color:#FF79C6">+</span><span style="color:#BD93F9"> 1</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">fn</span><span style="color:#50FA7B"> square</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#F8F8F2">    value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> MyParam</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> MyResult</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    value</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">pow</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">2</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">fn</span><span style="color:#50FA7B"> execute</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#F8F8F2">    f</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> &#x26;dyn</span><span style="color:#50FA7B"> Fn</span><span style="color:#F8F8F2">(</span><span style="color:#8BE9FD;font-style:italic">MyParam</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> MyResult</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> MyParam</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> MyResult</span></span>
<span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#50FA7B">    f</span><span style="color:#F8F8F2">(value)</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">fn</span><span style="color:#50FA7B"> main</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> value1 </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> execute</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">&#x26;</span><span style="color:#F8F8F2">inc, </span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> value2 </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> execute</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">&#x26;</span><span style="color:#F8F8F2">square, </span><span style="color:#BD93F9">5</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    println!</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">"{} {}"</span><span style="color:#F8F8F2">, value1, value2);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Thank you for reading! Follow me on <a href="https://twitter.com/daviddalbusco" rel="noopener noreferrer external">Twitter</a> for more exciting coding content.</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Chur - Dreibündenstein - Chur]]></title>
          <link>https://daviddalbusco.com/trails/2023/chur-dreibundenstein-chur/</link>
          <pubDate>Sun, 23 Jul 2023 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>An absolutely uncrowded and enjoyable run. It starts and ends at the Chur railway station, making it really easy to access. The first and last km on asphalt, but the rest is all trail.</p>
<p>Note that it is not the best-indicated run path I've ever followed. There aren't as many yellow or white-red indicators as usual.</p>
<hr />
<p>Waypoints:</p>
<p>Chur<br />
Juchs<br />
Brambüesch<br />
Dreibündenstein<br />
Juchs<br />
Chur</p>
<hr />
<p>There is an alternative path at the top which adds a few meters by going through Pradaschier before reaching Dreibündenstein, and by going to Lady Patricia after the summit.</p>
<p>It is also possible to not go back to Chur and avoid going down too much by taking the cable car back.</p>
<p>Such an alternative trail run is provided by the city of Chur: https://www.chur.graubuenden.ch/de/touren-planer/trailrun-langa</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Automatic Candid Generation in Rust: Exploring the ic_cdk v0.10.0 Update]]></title>
          <description><![CDATA[How to Automatically Generate Candid from Rust on the IC and Migrate Your Existing Project to Utilize the Latest Version of ic_cdk.]]></description>
          <link>https://daviddalbusco.com/blog/automatic-candid-generation-in-rust-exploring-the-ic-cdk-v0-10-0-update/</link>
          <pubDate>Fri, 21 Jul 2023 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*BJliLFjN_gUXOpMLsCQ1tQ.jpeg" alt="" / loading="lazy"></p>
<p>Photo by <a href="https://unsplash.com/@lightcircle?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Bilal O.</a> on <a href="https://unsplash.com/fr/photos/ljXekphwr40?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></p>
<hr />
<p>So, yesterday I upgraded <a href="https://juno.build/" rel="noopener noreferrer external">Juno</a> to the latest release of <a href="https://github.com/dfinity/cdk-rs/blob/main/src/ic-cdk/CHANGELOG.md#0100---2023-07-13" rel="noopener noreferrer external">ic_cdk</a> and discovered that the automatic generation of the Candid declarations needed an update. In this post, I will walk you through the process of migrating your project.</p>
<blockquote>
<p><strong>Update</strong>: On September 18th, 2023, a new version of the ic_cdk v0.11.0 has been released, and this article has been updated accordingly.</p>
</blockquote>
<h2>Previous Workaround</h2>
<p>If you have previously relied on automatic type generation, chances are you used the <code>export_service</code> crate and the workaround involving generating and writing the <code>did</code> files to the file system through a test by running <code>cargo test</code>.</p>
<p>I covered this approach in a <a href="https://daviddalbusco.com/blog/automatically-generate-candid-from-rust-on-the-ic/" rel="noopener noreferrer external">previous blog post</a> earlier this year, but to summarize, your code most probably looked like the following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">use</span><span style="color:#F8F8F2"> ic_cdk_macros</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">{query, update};</span></span>
<span class="line"><span style="color:#FF79C6">use</span><span style="color:#F8F8F2"> ic_cdk</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">export</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">candid</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">{candid_method};</span></span>
<span class="line"><span style="color:#FF79C6">use</span><span style="color:#F8F8F2"> ic_cdk</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">export</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">candid</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">{export_service};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// Your code</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">#[candid_method(query)]</span></span>
<span class="line"><span style="color:#F8F8F2">#[query]</span></span>
<span class="line"><span style="color:#FF79C6">fn</span><span style="color:#50FA7B"> hello</span><span style="color:#F8F8F2">(name</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> String</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> String</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    format!</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">"Hello, {}!"</span><span style="color:#F8F8F2">, name)</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">#[candid_method(update)]</span></span>
<span class="line"><span style="color:#F8F8F2">#[update]</span></span>
<span class="line"><span style="color:#FF79C6">fn</span><span style="color:#50FA7B"> world</span><span style="color:#F8F8F2">(name</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> String</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> String</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    format!</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">"World, {}!"</span><span style="color:#F8F8F2">, name)</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// The workaround to generate did files automatically</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">fn</span><span style="color:#50FA7B"> export_candid</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> String</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    export_service!</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#50FA7B">    __export_service</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">#[cfg(test)]</span></span>
<span class="line"><span style="color:#FF79C6">mod</span><span style="color:#F8F8F2"> tests {</span></span>
<span class="line"><span style="color:#FF79C6">    use</span><span style="color:#BD93F9;font-style:italic"> super</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">export_candid;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    #[test]</span></span>
<span class="line"><span style="color:#FF79C6">    fn</span><span style="color:#50FA7B"> save_candid</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        use</span><span style="color:#F8F8F2"> std</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">env;</span></span>
<span class="line"><span style="color:#FF79C6">        use</span><span style="color:#F8F8F2"> std</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">fs</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">write;</span></span>
<span class="line"><span style="color:#FF79C6">        use</span><span style="color:#F8F8F2"> std</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">path</span><span style="color:#FF79C6">::</span><span style="color:#8BE9FD;font-style:italic">PathBuf</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        let</span><span style="color:#F8F8F2"> dir </span><span style="color:#FF79C6">=</span><span style="color:#8BE9FD;font-style:italic"> PathBuf</span><span style="color:#FF79C6">::</span><span style="color:#50FA7B">from</span><span style="color:#F8F8F2">(env</span><span style="color:#FF79C6">::</span><span style="color:#50FA7B">var</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">"CARGO_MANIFEST_DIR"</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">unwrap</span><span style="color:#F8F8F2">());</span></span>
<span class="line"><span style="color:#FF79C6">        let</span><span style="color:#F8F8F2"> dir </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> dir</span></span>
<span class="line"><span style="color:#FF79C6">            .</span><span style="color:#50FA7B">parent</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#FF79C6">            .</span><span style="color:#50FA7B">unwrap</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#FF79C6">            .</span><span style="color:#50FA7B">parent</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#FF79C6">            .</span><span style="color:#50FA7B">unwrap</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#FF79C6">            .</span><span style="color:#50FA7B">join</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">"src"</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#FF79C6">            .</span><span style="color:#50FA7B">join</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">"demo"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#50FA7B">        write</span><span style="color:#F8F8F2">(dir</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">join</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">"demo.did"</span><span style="color:#F8F8F2">), </span><span style="color:#50FA7B">export_candid</span><span style="color:#F8F8F2">())</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">expect</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">"Write failed."</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><h2>New Solution</h2>
<p>No more workarounds! ic_cdk v0.10.0 introduces a new macro called <code>export_candid</code>, designed specifically to facilitate automatic generation of the <code>did</code> files.</p>
<p>Therefore, if you are migrating or starting a new canister and want to generate the types automatically, the new solution basically consists of adding <code>export_candid!()</code> at the end of <code>lib.rs</code>, and that's all you need for the code.</p>
<p>However, it’s worth noting that the effective creation of the files is not yet integrated into <code>dfx</code>. As a result, you will need a script to generate them.</p>
<p>But no worries, I will spare you the hassle of building such a script and share a solution with you in the next chapter.</p>
<h2>Migration Guide</h2>
<p>If you are following this blog post to generate did files automatically for a new canister, you can skip the first steps and jump directly to step 4. However, if you are migrating an existing project that implements the workaround, begin by removing it:</p>
<ol>
<li><p>Remove all <code>#[candid_method(query)]</code> and <code>#[candid_method(update)]</code> from your code. These attributes are no longer required and, in fact, they conflict with the new solution.</p></li>
<li><p>Delete the all workaround <code>export_service</code> and <code>save_candid</code> test.</p></li>
<li><p>Suppress the related imports (<code>use ic_cdk::export::candid::{export_service, candid_method}</code>) that have become unused.</p></li>
<li><p>Import and add the <code>export_candid</code> macro at the end of your <code>lib.rs</code></p></li>
</ol>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">use</span><span style="color:#F8F8F2"> ic_cdk_macros</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">{query, update, export_candid};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// Your code</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">#[query]</span></span>
<span class="line"><span style="color:#FF79C6">fn</span><span style="color:#50FA7B"> hello</span><span style="color:#F8F8F2">(name</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> String</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> String</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    format!</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">"Hello, {}!"</span><span style="color:#F8F8F2">, name)</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">#[update]</span></span>
<span class="line"><span style="color:#FF79C6">fn</span><span style="color:#50FA7B"> world</span><span style="color:#F8F8F2">(name</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> String</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> String</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    format!</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">"World, {}!"</span><span style="color:#F8F8F2">, name)</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// Generate did files</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">export_candid!</span><span style="color:#F8F8F2">();</span></span></code></pre><ol start="5">
<li>Install <a href="https://crates.io/crates/candid-extractor" rel="noopener noreferrer external">candid-extractor</a>, a new CLI tool to extract candid definition from canister WASM.</li>
</ol>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">cargo</span><span style="color:#F1FA8C"> install</span><span style="color:#F1FA8C"> candid-extractor</span></span></code></pre><ol start="6">
<li>Add the following script to your project or copy it from Juno’s repo: <a href="https://github.com/junobuild/juno/blob/main/scripts/did.sh" rel="noopener noreferrer external">https://github.com/junobuild/juno/blob/main/scripts/did.sh</a></li>
</ol>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">#!/usr/bin/env bash</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">function</span><span style="color:#50FA7B"> generate_did</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    local</span><span style="color:#BD93F9"> canister</span><span style="color:#FF79C6">=</span><span style="color:#FFB86C;font-style:italic">$1</span></span>
<span class="line"><span style="color:#BD93F9">    canister_root</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">src/</span><span style="color:#BD93F9">$canister</span><span style="color:#E9F284">"</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    cargo</span><span style="color:#F1FA8C"> build</span><span style="color:#BD93F9"> --manifest-path=</span><span style="color:#E9F284">"</span><span style="color:#BD93F9">$canister_root</span><span style="color:#F1FA8C">/Cargo.toml</span><span style="color:#E9F284">"</span><span style="color:#FF79C6"> \</span></span>
<span class="line"><span style="color:#BD93F9">    --target</span><span style="color:#F1FA8C"> wasm32-unknown-unknown</span><span style="color:#FF79C6"> \</span></span>
<span class="line"><span style="color:#BD93F9">    --release</span><span style="color:#BD93F9"> --package</span><span style="color:#E9F284"> "</span><span style="color:#BD93F9">$canister</span><span style="color:#E9F284">"</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    candid-extractor</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">target/wasm32-unknown-unknown/release/</span><span style="color:#BD93F9">$canister</span><span style="color:#F1FA8C">.wasm</span><span style="color:#E9F284">"</span><span style="color:#FF79C6"> ></span><span style="color:#E9F284"> "</span><span style="color:#BD93F9">$canister_root</span><span style="color:#F1FA8C">/</span><span style="color:#BD93F9">$canister</span><span style="color:#F1FA8C">.did</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4"># The list of canisters of your project</span></span>
<span class="line"><span style="color:#BD93F9">CANISTERS</span><span style="color:#FF79C6">=</span><span style="color:#F1FA8C">console,observatory,mission_control,satellite</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">for</span><span style="color:#BD93F9"> canister</span><span style="color:#FF79C6"> in</span><span style="color:#F8F8F2"> $(</span><span style="color:#8BE9FD">echo</span><span style="color:#BD93F9"> $CANISTERS</span><span style="color:#FF79C6"> |</span><span style="color:#50FA7B"> sed</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">s/,/ /g</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#FF79C6">do</span></span>
<span class="line"><span style="color:#50FA7B">  generate_did</span><span style="color:#E9F284"> "</span><span style="color:#BD93F9">$canister</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#FF79C6">done</span></span></code></pre><ol start="7">
<li><p>Optional: ⭐️ star <a href="https://github.com/junobuild/juno" rel="noopener noreferrer external">Juno’s repo on GitHub</a> and show your support!</p></li>
<li><p>Update the scripts variable <code>CANISTERS</code> with the names of the canisters in your project (comma separated list).</p></li>
</ol>
<p>That’s it, you’re all set! You can now run <code>$ did.sh</code> in your terminal to generate the <code>did</code> files for your project 🎉.</p>
<p>Personally, I like to automate and chain tasks, including the generation of the related formatted JavaScript file. That’s why I also suggest setting up the following scripts in your <code>package.json</code> to go a step further:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#E9F284">"</span><span style="color:#F1FA8C">scripts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">: {</span></span>
<span class="line"><span style="color:#8BE9FE">  "</span><span style="color:#8BE9FD">format</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">prettier --write .</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">  "</span><span style="color:#8BE9FD">generate</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">scripts/did.sh &#x26;&#x26; dfx generate &#x26;&#x26; npm run format</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>That way, you can now run <code>npm run generate</code> which will take care of everything to have everything set up for implementing your frontend.</p>
<h2>Known Errors and Solutions</h2>
<p>During the migration process, I encountered various issues. Below are the problems I faced along with their respective solutions, in case you encounter similar challenges:</p>
<h3><strong>duplicate method name</strong></h3>
<p>If you have forgotten to remove the attribute macro <code>#[candid_method]</code> from your code, running the <code>did.sh</code> script may result in an error: <code>duplicate method name</code> and <code>cannot find function</code>. Make sure to remove the attribute macro to resolve this issue (see step 1. of previous chapter).</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">error: duplicate method name del_controllers</span></span>
<span class="line"><span style="color:#FF79C6">   --></span><span style="color:#F8F8F2"> src</span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2">console</span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2">src</span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2">lib.rs:</span><span style="color:#BD93F9">232</span><span style="color:#F8F8F2">:</span><span style="color:#BD93F9">4</span></span>
<span class="line"><span style="color:#FF79C6">    |</span></span>
<span class="line"><span style="color:#BD93F9">232</span><span style="color:#FF79C6"> |</span><span style="color:#F8F8F2"> fn </span><span style="color:#50FA7B">del_controllers</span><span style="color:#F8F8F2">(DeleteControllersArgs { controllers }: DeleteControllersArgs) {</span></span>
<span class="line"><span style="color:#FF79C6">    |</span><span style="color:#FF79C6">    ^^^^^^^^^^^^^^^</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">error[E0425]: cannot find </span><span style="color:#FF79C6">function</span><span style="color:#F8F8F2"> `</span><span style="color:#50FA7B">del_controllers</span><span style="color:#F8F8F2">` </span><span style="color:#50FA7B">in</span><span style="color:#50FA7B"> this</span><span style="color:#50FA7B"> scope</span></span>
<span class="line"><span style="color:#F8F8F2">   --> </span><span style="color:#50FA7B">src</span><span style="color:#F8F8F2">/</span><span style="color:#50FA7B">console</span><span style="color:#F8F8F2">/</span><span style="color:#50FA7B">src</span><span style="color:#F8F8F2">/</span><span style="color:#50FA7B">lib</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">rs</span><span style="color:#F8F8F2">:232:4</span></span></code></pre><h3>candid::types::reference::Func</h3>
<p>If your canister implements a streaming strategy, you might encounter the following error:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">Error: failed to run main </span><span style="color:#FF79C6">module</span><span style="color:#F1FA8C"> `target/wasm32-unknown-unknown/release/satellite.wasm`</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">Caused</span><span style="color:#8BE9FD;font-style:italic"> by</span><span style="color:#F8F8F2">:</span></span>
<span class="line"><span style="color:#F8F8F2">    0: </span><span style="color:#8BE9FD;font-style:italic">failed</span><span style="color:#8BE9FD;font-style:italic"> to</span><span style="color:#8BE9FD;font-style:italic"> invoke</span><span style="color:#8BE9FD;font-style:italic"> command</span><span style="color:#8BE9FD;font-style:italic"> default</span></span>
<span class="line"><span style="color:#F8F8F2">    1: </span><span style="color:#8BE9FD;font-style:italic">error</span><span style="color:#8BE9FD;font-style:italic"> while</span><span style="color:#8BE9FD;font-style:italic"> executing</span><span style="color:#8BE9FD;font-style:italic"> at</span><span style="color:#8BE9FD;font-style:italic"> wasm</span><span style="color:#8BE9FD;font-style:italic"> backtrace</span><span style="color:#F8F8F2">:</span></span>
<span class="line"><span style="color:#F8F8F2">           0: 0</span><span style="color:#8BE9FD;font-style:italic">x204828</span><span style="color:#F8F8F2"> - &#x3C;</span><span style="color:#8BE9FD;font-style:italic">unknown</span><span style="color:#F8F8F2">>!</span><span style="color:#8BE9FD;font-style:italic">__rust_start_panic</span></span>
<span class="line"><span style="color:#F8F8F2">           1: 0</span><span style="color:#8BE9FD;font-style:italic">x2047f7</span><span style="color:#F8F8F2"> - &#x3C;</span><span style="color:#8BE9FD;font-style:italic">unknown</span><span style="color:#F8F8F2">>!</span><span style="color:#8BE9FD;font-style:italic">rust_panic</span></span>
<span class="line"><span style="color:#F8F8F2">           2: 0</span><span style="color:#8BE9FD;font-style:italic">x2047c7</span><span style="color:#F8F8F2"> - &#x3C;</span><span style="color:#8BE9FD;font-style:italic">unknown</span><span style="color:#F8F8F2">>!</span><span style="color:#8BE9FD;font-style:italic">std</span><span style="color:#F8F8F2">::</span><span style="color:#8BE9FD;font-style:italic">panicking</span><span style="color:#F8F8F2">::</span><span style="color:#8BE9FD;font-style:italic">rust_panic_with_hook</span><span style="color:#F8F8F2">::</span><span style="color:#8BE9FD;font-style:italic">h70a0e195f4db2a29</span></span>
<span class="line"><span style="color:#F8F8F2">           3: 0</span><span style="color:#8BE9FD;font-style:italic">x203d2b</span><span style="color:#F8F8F2"> - &#x3C;</span><span style="color:#8BE9FD;font-style:italic">unknown</span><span style="color:#F8F8F2">>!</span><span style="color:#8BE9FD;font-style:italic">std</span><span style="color:#F8F8F2">::</span><span style="color:#8BE9FD;font-style:italic">panicking</span><span style="color:#F8F8F2">::</span><span style="color:#8BE9FD;font-style:italic">begin_panic_handler</span><span style="color:#F8F8F2">::{{closure}}::hdcfc819ce836829e</span></span>
<span class="line"><span style="color:#BD93F9">           4</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">0x203c90</span><span style="color:#FF79C6"> -</span><span style="color:#FF79C6"> &#x3C;</span><span style="color:#F8F8F2">unknown</span><span style="color:#FF79C6">>!</span><span style="color:#F8F8F2">std::sys_common::backtrace::__rust_end_short_backtrace::h53cabafab5b09ada</span></span>
<span class="line"><span style="color:#BD93F9">           5</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">0x204432</span><span style="color:#FF79C6"> -</span><span style="color:#FF79C6"> &#x3C;</span><span style="color:#F8F8F2">unknown</span><span style="color:#FF79C6">>!</span><span style="color:#F8F8F2">rust_begin_unwind</span></span>
<span class="line"><span style="color:#BD93F9">           6</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">0x205b2f</span><span style="color:#FF79C6"> -</span><span style="color:#FF79C6"> &#x3C;</span><span style="color:#F8F8F2">unknown</span><span style="color:#FF79C6">>!</span><span style="color:#F8F8F2">core::panicking::panic_fmt::h751be80779d42b53</span></span>
<span class="line"><span style="color:#BD93F9">           7</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">0x1dc870</span><span style="color:#FF79C6"> -</span><span style="color:#FF79C6"> &#x3C;</span><span style="color:#F8F8F2">unknown</span><span style="color:#FF79C6">>!&#x3C;</span><span style="color:#F8F8F2">candid::types::reference::Func </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> candid</span><span style="color:#F8F8F2">::types::CandidType</span><span style="color:#FF79C6">></span><span style="color:#F8F8F2">::_ty::h5a3086fe78ee70eb</span></span>
<span class="line"><span style="color:#BD93F9">           8</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">0x734f4</span><span style="color:#FF79C6"> -</span><span style="color:#FF79C6"> &#x3C;</span><span style="color:#F8F8F2">unknown</span><span style="color:#FF79C6">>!</span><span style="color:#F8F8F2">candid::types::CandidType::ty::h3629ad9f5296022d</span></span>
<span class="line"><span style="color:#BD93F9">           9</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">0x983bd</span><span style="color:#FF79C6"> -</span><span style="color:#FF79C6"> &#x3C;</span><span style="color:#F8F8F2">unknown</span><span style="color:#FF79C6">>!</span><span style="color:#F8F8F2">candid::types::CandidType::ty::h9570a7b7bc1b89e8</span></span>
<span class="line"><span style="color:#BD93F9">          10</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">0xace19</span><span style="color:#FF79C6"> -</span><span style="color:#FF79C6"> &#x3C;</span><span style="color:#F8F8F2">unknown</span><span style="color:#FF79C6">>!</span><span style="color:#F8F8F2">candid::types::CandidType::ty::hba90fc116dda9bd1</span></span>
<span class="line"><span style="color:#BD93F9">          11</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">0x95616</span><span style="color:#FF79C6"> -</span><span style="color:#FF79C6"> &#x3C;</span><span style="color:#F8F8F2">unknown</span><span style="color:#FF79C6">>!</span><span style="color:#F8F8F2">candid::types::CandidType::ty::hee774e983a92def6</span></span>
<span class="line"><span style="color:#BD93F9">          12</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">0x607d3</span><span style="color:#FF79C6"> -</span><span style="color:#FF79C6"> &#x3C;</span><span style="color:#F8F8F2">unknown</span><span style="color:#FF79C6">>!</span><span style="color:#F8F8F2">_start</span></span>
<span class="line"><span style="color:#F8F8F2">       note: </span><span style="color:#FF79C6">using</span><span style="color:#F8F8F2"> the </span><span style="color:#F1FA8C">`WASMTIME_BACKTRACE_DETAILS=1`</span><span style="color:#F8F8F2"> environment variable may show more debugging information</span></span>
<span class="line"><span style="color:#BD93F9">    2</span><span style="color:#F8F8F2">: wasm trap: wasm </span><span style="color:#F1FA8C">`unreachable`</span><span style="color:#F8F8F2"> instruction executed</span></span></code></pre><p>If you do face such issue, the root cause is probably the usage of the <code>candid::Func</code> in the declaration of the strategy:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">#[derive(</span><span style="color:#8BE9FD;font-style:italic">CandidType</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">Deserialize</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">Clone</span><span style="color:#F8F8F2">)]</span></span>
<span class="line"><span style="color:#FF79C6">pub</span><span style="color:#FF79C6"> enum</span><span style="color:#8BE9FD;font-style:italic"> StreamingStrategy</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">    Callback</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        token</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> StreamingCallbackToken</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        callback</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Func</span><span style="color:#F8F8F2">, </span><span style="color:#6272A4">// &#x3C;------------- root cause of the issue</span></span>
<span class="line"><span style="color:#F8F8F2">    },</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>This can be solve by using for example a <code>define_function!</code>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">use</span><span style="color:#F8F8F2"> candid</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">{define_function, </span><span style="color:#8BE9FD;font-style:italic">CandidType</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">Deserialize</span><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">define_function!</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">pub</span><span style="color:#8BE9FD;font-style:italic"> CallbackFunc</span><span style="color:#FF79C6"> :</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">-></span><span style="color:#F8F8F2"> () query);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">#[derive(</span><span style="color:#8BE9FD;font-style:italic">CandidType</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">Deserialize</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">Clone</span><span style="color:#F8F8F2">)]</span></span>
<span class="line"><span style="color:#FF79C6">pub</span><span style="color:#FF79C6"> enum</span><span style="color:#8BE9FD;font-style:italic"> StreamingStrategy</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">    Callback</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        token</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> StreamingCallbackToken</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        callback</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> CallbackFunc</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    },</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p><em>Note that the recommended signature for <code>CallbackFunc</code> is most probably <code>(ArgToken) -&gt; (StreamingCallbackHttpresponse) query</code> but the above does the job for me.</em></p>
<p>However, by modifying the type, the usage of the strategy will most probably need to be updated as well. For example, this was my original usage:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">pub</span><span style="color:#FF79C6"> fn</span><span style="color:#50FA7B"> streaming_strategy</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#F8F8F2">    key</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#8BE9FD;font-style:italic">AssetKey</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    encoding</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#8BE9FD;font-style:italic">AssetEncoding</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    encoding_type</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#8BE9FD;font-style:italic">str</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    headers</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#F8F8F2">[</span><span style="color:#8BE9FD;font-style:italic">HeaderField</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> Option</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">StreamingStrategy</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> streaming_token</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Option</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">StreamingCallbackToken</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#50FA7B">        create_token</span><span style="color:#F8F8F2">(key, </span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">, encoding, encoding_type, headers);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    streaming_token</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">map</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">|</span><span style="color:#F8F8F2">streaming_token</span><span style="color:#FF79C6">|</span><span style="color:#F8F8F2"> StreamingStrategy</span><span style="color:#FF79C6">::</span><span style="color:#8BE9FD;font-style:italic">Callback</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        callback</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> CallbackFunc</span><span style="color:#F8F8F2"> { </span><span style="color:#6272A4">// &#x3C;----------- Error fields are missing</span></span>
<span class="line"><span style="color:#F8F8F2">            method</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> "http_request_streaming_callback"</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">to_string</span><span style="color:#F8F8F2">(),</span></span>
<span class="line"><span style="color:#F8F8F2">            principal</span><span style="color:#FF79C6">:</span><span style="color:#50FA7B"> id</span><span style="color:#F8F8F2">(),</span></span>
<span class="line"><span style="color:#F8F8F2">        },</span></span>
<span class="line"><span style="color:#F8F8F2">        token</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> streaming_token,</span></span>
<span class="line"><span style="color:#F8F8F2">    })</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Which I had to convert to following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">pub</span><span style="color:#FF79C6"> fn</span><span style="color:#50FA7B"> streaming_strategy</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#F8F8F2">    key</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#8BE9FD;font-style:italic">AssetKey</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    encoding</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#8BE9FD;font-style:italic">AssetEncoding</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    encoding_type</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#8BE9FD;font-style:italic">str</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    headers</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#F8F8F2">[</span><span style="color:#8BE9FD;font-style:italic">HeaderField</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> Option</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">StreamingStrategy</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> streaming_token</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Option</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">StreamingCallbackToken</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#50FA7B">        create_token</span><span style="color:#F8F8F2">(key, </span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">, encoding, encoding_type, headers);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    streaming_token</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">map</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">|</span><span style="color:#F8F8F2">streaming_token</span><span style="color:#FF79C6">|</span><span style="color:#F8F8F2"> StreamingStrategy</span><span style="color:#FF79C6">::</span><span style="color:#8BE9FD;font-style:italic">Callback</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        callback</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> CallbackFunc</span><span style="color:#FF79C6">::</span><span style="color:#50FA7B">new</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">id</span><span style="color:#F8F8F2">(), </span><span style="color:#F1FA8C">"http_request_streaming_callback"</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">to_string</span><span style="color:#F8F8F2">()),</span></span>
<span class="line"><span style="color:#F8F8F2">        token</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> streaming_token,</span></span>
<span class="line"><span style="color:#F8F8F2">    })</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><h3>unknown import: <code>ic0::stable64_size</code></h3>
<p>Finally, if you are using <a href="https://github.com/dfinity/stable-structures" rel="noopener noreferrer external">stable structures</a>, you might encounter the following issue:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">Error: failed to run main </span><span style="color:#FF79C6">module</span><span style="color:#F1FA8C"> `target/wasm32-unknown-unknown/release/orbiter.wasm`</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">Caused</span><span style="color:#8BE9FD;font-style:italic"> by</span><span style="color:#F8F8F2">:</span></span>
<span class="line"><span style="color:#F8F8F2">    0: </span><span style="color:#8BE9FD;font-style:italic">failed</span><span style="color:#8BE9FD;font-style:italic"> to</span><span style="color:#8BE9FD;font-style:italic"> instantiate</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">target/wasm32-unknown-unknown/release/orbiter.wasm</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    1: </span><span style="color:#8BE9FD;font-style:italic">unknown</span><span style="color:#8BE9FD;font-style:italic"> import</span><span style="color:#F8F8F2">: </span><span style="color:#F1FA8C">`ic0::stable64_size`</span><span style="color:#8BE9FD;font-style:italic"> has</span><span style="color:#8BE9FD;font-style:italic"> not</span><span style="color:#8BE9FD;font-style:italic"> been</span><span style="color:#8BE9FD;font-style:italic"> defined</span></span></code></pre><p>This issue was present in ic_cdk v0.10.0 but has been resolved in v0.11.0 🥳.</p>
<hr />
<p>Thanks for reading! Follow me on <a href="https://twitter.com/daviddalbusco" rel="noopener noreferrer external">Twitter</a> for more coding content and reach out if you have any questions.</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Exploring a Juno’s Web3 Dapp Architecture]]></title>
          <description><![CDATA[I built a web app on the Internet Computer blockchain in a weekend with Juno. Check out my insights into the frontend architecture! 🛰️]]></description>
          <link>https://daviddalbusco.com/blog/exploring-a-junos-web3-dapp-architecture/</link>
          <pubDate>Fri, 23 Jun 2023 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*qBDGBb6aklck9uiOSzezPA.jpeg" alt="" / loading="lazy"></p>
<hr />
<p>I developed a web-based drawing application — <a href="https://icdraw.com/" rel="noopener noreferrer external">icdraw.com</a> — on the <a href="https://internetcomputer.org/" rel="noopener noreferrer external">Internet Computer</a> blockchain in just a single weekend, thanks to the smooth integration provided by <a href="https://juno.build/" rel="noopener noreferrer external">Juno</a> 🛰️. In this blog post, I present some insights into the architecture of the frontend application I’ve built.</p>
<hr />
<h2>Architecture</h2>
<p>From a fundamental perspective, developing a JavaScript frontend application running entirely on blockchain using Juno does not differ significantly in terms of architecture compared to traditional Web2 serverless solutions.</p>
<p>At the heart of the application lies a library responsible for evaluating the user’s authentication status, enabling or disabling access to different features based on this assessment. Each of these features has the capability to interact with and manipulate data and files on the blockchain, allowing for read, write, update, or delete operations. The only minor distinction is that modifying a state on the blockchain introduces a slight delay of a few seconds compared to saving data on a centralized server. This delay is due to the requirement of certifying the information through a consensus round.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*jgPnSRFcW4O46jlCDN4Bkg.png" alt="" / loading="lazy"></p>
<p>While there are no significant other differences, one crucial aspect stands out: my personal aversion to using applications that necessitate “manual save.” To address this, I envisioned and implemented an auto-save feature.</p>
<p>However, as I understand that data persistence requires resources, I strategically incorporated this process within a Web Worker. By doing so, the burden of this task is shifted away from the UI, ensuring a seamless user experience.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*4vTielgW4OSOTi97000Xkw.png" alt="" / loading="lazy"></p>
<p>Instead of implementing an approach where the UI triggers any changes by directly interacting with the worker, I opted for a different approach in this project. Upon app initialization, a cron timer is initiated within the web worker. This timer periodically checks for any changes and processes the save operation if any modifications are detected.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*lQ132OMsoLHeL1lB_jjgKg.png" alt="" / loading="lazy"></p>
<p>Due to Juno’s ability to simplify and abstract the backend complexities associated with interfacing with the Internet Computer, there are no particular architectural details of the project really worth mentioning in my opinion. However, as the application was developed using React, I can provide some insights into the code structure.</p>
<hr />
<h2>Code Structure</h2>
<p>In my development process, I generally adopt a code organization strategy based on layers rather than features. It is worth noting that this practice may not be considered a current best practice, as it probably originates from my past experience working on projects using Java with Maven, quite some time ago 😅.</p>
<p>In the case of this project developed with React, the code was structured as follows:</p>
<p><img src="https://daviddalbusco.com/assets/images/1*5McpLM70W97RxCX6lEOVsQ.png" alt="" / loading="lazy"></p>
<p>I developed the UI using several independent and reusable UI components. Rather than relying on an external state management system, I opted to utilize a few React contexts for the few information that needed to be available globally.</p>
<hr />
<h2>Context</h2>
<p>The integration process of Juno begins with a straightforward initialization at the root of the project, specifically in App.tsx , connecting the application to its associated smart contract, which we affectionately named “Satellite.” Once connected, it gains the ability to interact with the blockchain.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { initJuno } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@junobuild/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { useEffect, useState } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Scene } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./components/Scene.tsx</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Auth } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./components/context/Auth.tsx</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Worker } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./components/context/Worker.tsx</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> App</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> [ready, setReady] </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useState</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">false</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  useEffect</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    (</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">      await</span><span style="color:#50FA7B"> initJuno</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        satelliteId</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">fqotu-wqaaa-aaaal-acp3a-cai</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">      });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">      setReady</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">true</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    })();</span></span>
<span class="line"><span style="color:#F8F8F2">  }, []);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Auth</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Worker</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#FF79C6">        &#x3C;</span><span style="color:#F8F8F2">Scene ready</span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2">{ready} </span><span style="color:#FF79C6">/></span></span>
<span class="line"><span style="color:#FF79C6">      &#x3C;/</span><span style="color:#F8F8F2">Worker</span><span style="color:#FF79C6">></span></span>
<span class="line"><span style="color:#FF79C6">    &#x3C;/</span><span style="color:#F8F8F2">Auth</span><span style="color:#FF79C6">></span></span>
<span class="line"><span style="color:#F8F8F2">  );</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> App;</span></span></code></pre><p>In the code snippet mentioned above, you may have noticed the use of two contexts:</p>
<ul>
<li><p>Auth: Handles the loading and synchronization of the user’s authentication state.</p></li>
<li><p>Worker: Manages the initialization and communication with the web workers.</p></li>
</ul>
<hr />
<h3>Authentication Context</h3>
<p>Juno offers a convenient observable function called authSubscribe() which provides information about the user’s sign-in status. To replicate this status across the entire application, I leveraged an effect and context mechanism. This allowed me to effectively propagate the user’s sign-in status throughout the app.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { authSubscribe, User } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@junobuild/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { createContext, ReactNode, useEffect, useState } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Spinner } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">../misc/Spinner.tsx</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> styles </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./Auth.module.scss</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> AuthContext </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> createContext</span><span style="color:#F8F8F2">&#x3C;{</span></span>
<span class="line"><span style="color:#F8F8F2">  user</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#FF79C6"> |</span><span style="color:#FFB86C;font-style:italic"> User</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  setBusy</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#FF79C6">    |</span><span style="color:#FFB86C;font-style:italic"> React</span><span style="color:#F8F8F2">.</span><span style="color:#FFB86C;font-style:italic">Dispatch</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">React</span><span style="color:#F8F8F2">.</span><span style="color:#FFB86C;font-style:italic">SetStateAction</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">boolean</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">>></span></span>
<span class="line"><span style="color:#FF79C6">    |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}>({</span></span>
<span class="line"><span style="color:#F8F8F2">  user</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  setBusy</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> Auth</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">children</span><span style="color:#F8F8F2"> }</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { children</span><span style="color:#FF79C6">?:</span><span style="color:#8BE9FD;font-style:italic"> ReactNode</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> [user, setUser] </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useState</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">User</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">>(</span><span style="color:#BD93F9">undefined</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> [busy, setBusy] </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useState</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">boolean</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">>(</span><span style="color:#BD93F9">undefined</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  useEffect</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> sub </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> authSubscribe</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">user</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> setUser</span><span style="color:#F8F8F2">(user));</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> sub</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  }, []);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#FF79C6">    &#x3C;</span><span style="color:#F8F8F2">AuthContext.Provider value</span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2">{{ user, setBusy }}</span><span style="color:#FF79C6">></span></span>
<span class="line"><span style="color:#F8F8F2">      {</span><span style="color:#FFB86C;font-style:italic">children</span><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">      {</span><span style="color:#FFB86C;font-style:italic">busy</span><span style="color:#F8F8F2"> ? (</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FFB86C;font-style:italic">div</span><span style="color:#FFB86C;font-style:italic"> className</span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2">{styles.loading}</span><span style="color:#FF79C6">></span></span>
<span class="line"><span style="color:#F8F8F2">          &#x3C;</span><span style="color:#FFB86C;font-style:italic">Spinner</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#FFB86C;font-style:italic">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      ) : </span><span style="color:#FFB86C;font-style:italic">undefined</span><span style="color:#F8F8F2">}</span></span>
<span class="line"><span style="color:#FF79C6">    &#x3C;/</span><span style="color:#F8F8F2">AuthContext.Provider</span><span style="color:#FF79C6">></span></span>
<span class="line"><span style="color:#F8F8F2">  );</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>In addition to the authentication functionality, I also incorporated a “busy” indicator within the context. This indicator displays a backdrop and spinner over the content when a sign-in process is in progress. But hey, I developed the project over a weekend, so I had to make some trade-offs.</p>
<hr />
<h3>Worker Context</h3>
<p>Implementing the initialization and interaction with web workers within a context was a new experience for me, and I must say, it’s pretty cool. In fact, I might even consider writing a separate blog post about it on my personal website because it’s an intriguing topic worth exploring further.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  createContext,</span></span>
<span class="line"><span style="color:#F8F8F2">  ReactNode,</span></span>
<span class="line"><span style="color:#F8F8F2">  useContext,</span></span>
<span class="line"><span style="color:#F8F8F2">  useEffect,</span></span>
<span class="line"><span style="color:#F8F8F2">  useState,</span></span>
<span class="line"><span style="color:#F8F8F2">} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  PostMessage,</span></span>
<span class="line"><span style="color:#F8F8F2">  PostMessageDataResponse,</span></span>
<span class="line"><span style="color:#F8F8F2">} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">../../types/post-message.ts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { AuthContext } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./Auth.tsx</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> WorkerContext </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> createContext</span><span style="color:#F8F8F2">&#x3C;{</span></span>
<span class="line"><span style="color:#F8F8F2">  worker</span><span style="color:#FF79C6">:</span><span style="color:#FFB86C;font-style:italic"> Worker</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  busy</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}>({</span></span>
<span class="line"><span style="color:#F8F8F2">  worker</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  busy</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> Worker</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">children</span><span style="color:#F8F8F2"> }</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { children</span><span style="color:#FF79C6">?:</span><span style="color:#8BE9FD;font-style:italic"> ReactNode</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> [worker, setWorker] </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useState</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Worker</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">>(</span><span style="color:#BD93F9">undefined</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> [busy, setBusy] </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useState</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">boolean</span><span style="color:#F8F8F2">>(</span><span style="color:#BD93F9">false</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> { user } </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useContext</span><span style="color:#F8F8F2">(AuthContext);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  useEffect</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    (</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">      let</span><span style="color:#F8F8F2"> w</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Worker</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> worker;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">      // Lazy load Web Worker</span></span>
<span class="line"><span style="color:#FF79C6">      if</span><span style="color:#F8F8F2"> (w </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> Worker </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#FF79C6"> import</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">../../workers/worker?worker</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">        w </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#F8F8F2"> Worker.</span><span style="color:#50FA7B">default</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">        setWorker</span><span style="color:#F8F8F2">(w);</span></span>
<span class="line"><span style="color:#F8F8F2">      }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">      // Start or stop cron job</span></span>
<span class="line"><span style="color:#F8F8F2">      w?.</span><span style="color:#50FA7B">postMessage</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        msg</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> user </span><span style="color:#FF79C6">!==</span><span style="color:#BD93F9"> undefined</span><span style="color:#FF79C6"> &#x26;&#x26;</span><span style="color:#F8F8F2"> user </span><span style="color:#FF79C6">!==</span><span style="color:#BD93F9"> null</span><span style="color:#FF79C6"> ?</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">start</span><span style="color:#E9F284">"</span><span style="color:#FF79C6"> :</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">stop</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        data</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">          user,</span></span>
<span class="line"><span style="color:#F8F8F2">        },</span></span>
<span class="line"><span style="color:#F8F8F2">      });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">      // Receive messages from the worker</span></span>
<span class="line"><span style="color:#FF79C6">      if</span><span style="color:#F8F8F2"> (w </span><span style="color:#FF79C6">!==</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">        w.</span><span style="color:#50FA7B">onmessage</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ({</span></span>
<span class="line"><span style="color:#F8F8F2">          data,</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> MessageEvent</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">PostMessage</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">PostMessageDataResponse</span><span style="color:#F8F8F2">>>) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">          const</span><span style="color:#F8F8F2"> { msg } </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> data;</span></span>
<span class="line"><span style="color:#50FA7B">          setBusy</span><span style="color:#F8F8F2">(msg </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">busy</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">        };</span></span>
<span class="line"><span style="color:#F8F8F2">      }</span></span>
<span class="line"><span style="color:#F8F8F2">    })();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> worker?.</span><span style="color:#50FA7B">postMessage</span><span style="color:#F8F8F2">({ msg</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">stop</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // eslint-disable-next-line react-hooks/exhaustive-deps</span></span>
<span class="line"><span style="color:#F8F8F2">  }, [user]);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#FF79C6">    &#x3C;</span><span style="color:#F8F8F2">WorkerContext.Provider value</span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2">{{ worker, busy }}</span><span style="color:#FF79C6">></span></span>
<span class="line"><span style="color:#F8F8F2">      {</span><span style="color:#FFB86C;font-style:italic">children</span><span style="color:#F8F8F2">}</span></span>
<span class="line"><span style="color:#FF79C6">    &#x3C;/</span><span style="color:#F8F8F2">WorkerContext.Provider</span><span style="color:#FF79C6">></span></span>
<span class="line"><span style="color:#F8F8F2">  );</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Within an effect, the user’s status passed through the previously defined context is observed. Based on whether the user is signed in or not, the worker is invoked to start or stop the cron job accordingly.</p>
<p>Furthermore, when the user is defined, it is passed to the worker using the postMessage method. This ensures that the worker performs its tasks only when the user is signed in too.</p>
<p>It’s worth noting that the web worker is lazily loaded. This approach was implemented to address bundling issues and ensure performance efficiency by loading the worker module only when necessary.</p>
<hr />
<h2>IndexedDB</h2>
<p>To save the edited data on the UI side and retrieve it in the worker before replicating it on the blockchain, I utilized IndexedDB with the assistance of one of my favorite libraries called <a href="https://github.com/jakearchibald/idb-keyval" rel="noopener noreferrer external">idb-keyval</a>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { createStore, get, setMany } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">idb-keyval</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Metadata } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">../types/app.ts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> stateStore </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> createStore</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">icdraw-state</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">state</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> KEY_SCENE </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">scene-key</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> KEY_NAME </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">scene-name</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> KEY_LAST_CHANGE </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">last-change</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// Not exhaustives functions example</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> setMetadata</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">key</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">name</span><span style="color:#F8F8F2"> }</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Metadata</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span></span>
<span class="line"><span style="color:#50FA7B">    setMany</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#F8F8F2">        [</span></span>
<span class="line"><span style="color:#F8F8F2">            [KEY_LAST_CHANGE, Date.</span><span style="color:#50FA7B">now</span><span style="color:#F8F8F2">()],</span></span>
<span class="line"><span style="color:#F8F8F2">            [KEY_SCENE, key],</span></span>
<span class="line"><span style="color:#F8F8F2">            [KEY_NAME, name]</span></span>
<span class="line"><span style="color:#F8F8F2">        ],</span></span>
<span class="line"><span style="color:#F8F8F2">        stateStore</span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> getLastChange</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">number</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> get</span><span style="color:#F8F8F2">(KEY_LAST_CHANGE, stateStore);</span></span></code></pre><p>Using the “last change” information, the cron job timer running in the web worker can determine if data needs to be saved on the blockchain or not.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">let</span><span style="color:#F8F8F2"> lastChangeProcessed</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> =</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> sync</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> lastChange </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> getLastChange</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (lastChange </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#6272A4">        // There weren't any changes</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (lastChangeProcessed </span><span style="color:#FF79C6">!==</span><span style="color:#BD93F9"> undefined</span><span style="color:#FF79C6"> &#x26;&#x26;</span><span style="color:#F8F8F2"> lastChange </span><span style="color:#FF79C6">&#x3C;=</span><span style="color:#F8F8F2"> lastChangeProcessed) {</span></span>
<span class="line"><span style="color:#6272A4">        // No new changes</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // Do the job</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    lastChangeProcessed </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> lastChange;</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><hr />
<h2>Web Worker</h2>
<p>As mentioned in the architecture chapter, the concept of the project is to continuously monitor for new changes rather than waiting for specific messages to trigger processes. Therefore, the worker receives only start and stop information and executes the necessary tasks accordingly.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">onmessage</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ({ data: { </span><span style="color:#FFB86C;font-style:italic">msg</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">data</span><span style="color:#F8F8F2"> } }</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> MessageEvent</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">PostMessage</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">PostMessageDataRequest</span><span style="color:#F8F8F2">>>) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    switch</span><span style="color:#F8F8F2"> (msg) {</span></span>
<span class="line"><span style="color:#FF79C6">        case</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">start</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">:</span></span>
<span class="line"><span style="color:#FF79C6">            await</span><span style="color:#50FA7B"> startTimer</span><span style="color:#F8F8F2">(data?.user);</span></span>
<span class="line"><span style="color:#FF79C6">            break</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">        case</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">stop</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">:</span></span>
<span class="line"><span style="color:#50FA7B">            stopTimer</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">            break</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">let</span><span style="color:#F8F8F2"> timer</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> NodeJS</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">Timeout</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> =</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> stopTimer</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">timer) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    clearInterval</span><span style="color:#F8F8F2">(timer);</span></span>
<span class="line"><span style="color:#F8F8F2">    timer </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> startTimer</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">user</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> User</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (user </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> null</span><span style="color:#FF79C6"> ||</span><span style="color:#F8F8F2"> user </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#6272A4">        // We do nothing if no user</span></span>
<span class="line"><span style="color:#F8F8F2">        console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Attempted to initiate a worker without a user.</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#50FA7B"> execute</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> sync</span><span style="color:#F8F8F2">(user);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // We start now but also schedule the update after wards</span></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#50FA7B"> execute</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    timer </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> setInterval</span><span style="color:#F8F8F2">(execute, </span><span style="color:#BD93F9">1000</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>On the UI side, Juno is initialized with initJuno which allow the library to automatically provide the information to authenticate each call made to the API smart contract. However, in the worker, I did not utilize this functionality. Instead, I manually constructed the necessary information required by the smart contract to validate the calls.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> sync</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">user</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> User</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">  // Some checks and logic</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> satellite </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">      identity</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> unsafeIdentity</span><span style="color:#F8F8F2">(),</span></span>
<span class="line"><span style="color:#F8F8F2">      satelliteId</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">fqotu-wqaaa-aaaal-acp3a-cai</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> doc </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> getDoc</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">JunoScene</span><span style="color:#F8F8F2">>({</span></span>
<span class="line"><span style="color:#F8F8F2">      collection</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">scenes</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">      key,</span></span>
<span class="line"><span style="color:#F8F8F2">      satellite,</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span></code></pre><p>The function responsible for resolving the identity is named unsafeIdentity because it returns imperative information that can include an anonymous identity. As such, it should only be used when necessary, and its specific name serves as a reminder to handle it with caution.</p>
<p>While the majority of the work was delegated to the web worker, I still wanted to visually indicate to the user when certain tasks were in progress. To achieve this, I implemented a messaging system where the worker sends messages to the UI indicating whether it is in a busy or idle state. This status information is then used to display a spinner and disable action buttons.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">// Web Worker</span></span>
<span class="line"><span style="color:#50FA7B">postMessage</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    msg</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">busy</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#6272A4">// or "idle"</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// UI</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { SyncOutlined } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@ant-design/icons</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { useContext } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { WorkerContext } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">../context/Worker.tsx</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> styles </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./Busy.module.scss</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> Busy</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> { busy } </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useContext</span><span style="color:#F8F8F2">(WorkerContext);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#FF79C6">    &#x3C;</span><span style="color:#F8F8F2">div className</span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2">{styles.busy}</span><span style="color:#FF79C6">></span></span>
<span class="line"><span style="color:#F8F8F2">      {</span><span style="color:#FFB86C;font-style:italic">busy</span><span style="color:#F8F8F2"> ? &#x3C;</span><span style="color:#FFB86C;font-style:italic">SyncOutlined</span><span style="color:#FFB86C;font-style:italic"> spin</span><span style="color:#F8F8F2"> /> : </span><span style="color:#FFB86C;font-style:italic">undefined</span><span style="color:#F8F8F2">}</span></span>
<span class="line"><span style="color:#FF79C6">    &#x3C;/</span><span style="color:#F8F8F2">div</span><span style="color:#FF79C6">></span></span>
<span class="line"><span style="color:#F8F8F2">  );</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Because the interaction with the worker was scoped and shared within a context, I could utilize this information in a component without the need to pass properties or reinstantiate the worker.</p>
<hr />
<h2>Resources</h2>
<p>The project I developed over the weekend, which I have described in this blog post, is open source, just like <a href="https://juno.build" rel="noopener noreferrer external">Juno</a>! 🤗</p>
<ul>
<li><p>icdraw 👉 <a href="https://github.com/peterpeterparker/icdraw" rel="noopener noreferrer external">https://github.com/peterpeterparker/icdraw</a></p></li>
<li><p>Juno 👉 <a href="https://github.com/junobuild/juno" rel="noopener noreferrer external">https://github.com/junobuild/juno</a></p></li>
</ul>
<p>To infinity and beyond<br />
David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Walensee]]></title>
          <link>https://daviddalbusco.com/trails/2023/walensee/</link>
          <pubDate>Sun, 04 Jun 2023 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>Finally, after three attempts spread over three years, I have successfully completed the entire Walensee loop by running!</p>
<p>The trail starts and ends in Weesen, which is accessible by bus.</p>
<p>The first half of the loop takes you through scenic trails, while the second half is on asphalt next to the lake.</p>
<p>Waypoints: Weesen - Walenstadtberg - Walenstadt - Murg - Weesen</p>
<p>Important note: There are two errors in the GPX data as I accidentally followed an incorrect path for a few hundred meters on two occasions (around 8km and 42km) during my run.</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Build a Web3 App with VueJS]]></title>
          <description><![CDATA[Empower your dapp development: Unleashing Vue and Juno for Web3 decentralized applications.]]></description>
          <link>https://daviddalbusco.com/blog/build-a-web3-app-with-vuejs/</link>
          <pubDate>Wed, 24 May 2023 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<h2>Introduction</h2>
<p>As a frontend JavaScript developer stepping into the decentralized web, you may have encountered numerous solutions for Web3 development. However, these solutions often focus on wallet integration and transaction execution, creating a learning curve and deviating from the familiar Web2 development experience.</p>
<p>But fear not! There's a solution that bridges the gap between Web2 and Web3 seamlessly - <a href="https://juno.build" rel="noopener noreferrer external">Juno</a>.</p>
<p>In this blog post, we will explore how to leverage the power of Vue and Juno to develop decentralized applications (dApps). By combining these technologies, you'll discover a user-friendly and efficient approach to building powerful dApps that harness the potential of Web3. Join us on this journey as we unveil the secrets of Juno and empower you to create remarkable decentralized experiences with ease!</p>
<hr />
<h2>Foreword</h2>
<p>In my previous blog posts, I discussed similar solutions for <a href="https://juno.build/blog/build-a-web3-app-with-react-js" rel="noopener noreferrer external">React</a> and <a href="https://juno.build/blog/develop-an-angular-app-on-blockchain" rel="noopener noreferrer external">Angular</a>, two popular JavaScript frontend frameworks. If either of these frameworks is your preferred choice, I encourage you to explore those specific posts for tailored insights.</p>
<hr />
<h2>How Juno Works</h2>
<p>If you're not yet acquainted with Juno, it's a powerful open-source Blockchain-as-a-Service platform designed to make decentralized application development more accessible. Think of it as a serverless platform, similar to popular services like Google Firebase or AWS Amplify, but with the added advantage of blockchain technology. Juno takes care of everything by running your applications entirely on the blockchain, ensuring a fully decentralized and secure infrastructure.</p>
<p>By leveraging the <a href="https://internetcomputer.org/" rel="noopener noreferrer external">Internet Computer</a> blockchain network and infrastructure, Juno introduces a unique concept called &quot;Satellites&quot; for each app you create. These Satellites act as robust smart contracts that encapsulate your entire application, including web assets like JavaScript, HTML, and image files, as well as a simple database, file storage, and authentication mechanisms. With Juno, you have complete control over your app's functionality and data.</p>
<hr />
<h2>Build A Dapp</h2>
<p>Let's start building our first decentralized application, or &quot;dapp&quot; for short. In this example, we will create a note-taking app that allows users to store and retrieve data entries, as well as upload files.</p>
<hr />
<h3>Introduction</h3>
<p>This tutorial and code sample utilize the Vue Composition API.</p>
<hr />
<h3>Initialization</h3>
<p>Before you can integrate Juno into your app, you'll need to create a satellite. This process is explained in detail in the <a href="https://juno.build/docs/add-juno-to-an-app/create-a-satellite" rel="noopener noreferrer external">documentation</a>.</p>
<p>Moreover, you also need to install the SDK.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> i</span><span style="color:#F1FA8C"> @junobuild/core</span></span></code></pre><p>After completing both of these steps, you can initialize Juno with your satellite ID at the root of your Vue app, for example in <code>App.vue</code>. This will configure the library to communicate with your smart contract.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#50FA7B;font-style:italic"> setup</span><span style="color:#50FA7B;font-style:italic"> lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#FF79C6">    import</span><span style="color:#F8F8F2"> { onMounted } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">vue</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">    import</span><span style="color:#F8F8F2"> { initJuno } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@junobuild/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    onMounted</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#FF79C6">        async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span></span>
<span class="line"><span style="color:#FF79C6">            await</span><span style="color:#50FA7B"> initJuno</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">                satelliteId</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">pycrs-xiaaa-aaaal-ab6la-cai</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">            })</span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">template</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">>Hello World&#x3C;/</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">template</span><span style="color:#F8F8F2">></span></span></code></pre><p>That's it for the configuration! Your app is now ready for Web3! 😎</p>
<hr />
<h3>Authentication</h3>
<p>In order to ensure secure and anonymous user identification, it is necessary for users to sign in and sign out. To accomplish this, you can bind the relevant functions to call-to-action buttons placed anywhere within your application.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#50FA7B;font-style:italic"> setup</span><span style="color:#50FA7B;font-style:italic"> lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#FF79C6">    import</span><span style="color:#F8F8F2"> { signIn, signOut } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@junobuild/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">button</span><span style="color:#50FA7B;font-style:italic"> @click</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">signIn</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Sign-in&#x3C;/</span><span style="color:#FF79C6">button</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">button</span><span style="color:#50FA7B;font-style:italic"> @click</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">signOut</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Sign-out&#x3C;/</span><span style="color:#FF79C6">button</span><span style="color:#F8F8F2">></span></span></code></pre><p>In order to establish seamless integration with other services, the library and satellite components automatically generate a new entry within your smart contract upon successful user sign-in. This functionality empowers the library to efficiently verify permissions during any data exchange.</p>
<p>To monitor and gain insights into this entry, and consequently access information about the user's state, Juno offers an observable function called <code>authSubscribe()</code>. You have the flexibility to utilize this function as many times as necessary. However, you can also create a store that effectively propagates the user information throughout your app.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { authSubscribe, </span><span style="color:#FF79C6">type</span><span style="color:#F8F8F2"> User } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@junobuild/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { defineStore } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">pinia</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { ref, </span><span style="color:#FF79C6">type</span><span style="color:#F8F8F2"> Ref } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">vue</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> useAuthStore </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> defineStore</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">auth</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> user</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Ref</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">User</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> ref</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">undefined</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> unsubscribe </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> authSubscribe</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">u</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> (user.value </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> u));</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> { user, unsubscribe };</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><p>By doing so, it becomes convenient to subscribe to it at the top level of your application.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#50FA7B;font-style:italic"> setup</span><span style="color:#50FA7B;font-style:italic"> lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#FF79C6">    import</span><span style="color:#F8F8F2"> { useAuthStore } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">../stores/auth.store</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">    import</span><span style="color:#F8F8F2"> { storeToRefs } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">pinia</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> store </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useAuthStore</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { user } </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> storeToRefs</span><span style="color:#F8F8F2">(store);</span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">template</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">template</span><span style="color:#50FA7B;font-style:italic"> v-if</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">user !== undefined &#x26;&#x26; user !== null</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">slot</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">/</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">template</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">template</span><span style="color:#50FA7B;font-style:italic"> v-else</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">>Not signed in.&#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">template</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">template</span><span style="color:#F8F8F2">></span></span></code></pre><hr />
<h3>Storing Documents</h3>
<p>Juno presents a feature called &quot;Datastore&quot; designed for storing data directly on the blockchain. A Datastore comprises a set of collections, where each collection holds documents, uniquely identified by a key of your choice.</p>
<p>In this tutorial, our objective is to store notes, so it is essential to create a collection in accordance with the instructions provided in the documentation. Choose an appropriate name for the collection, such as &quot;notes.&quot;</p>
<p>Once you have set up your application and created the necessary collection, you can utilize the library's <code>setDoc</code> function to persist data onto the blockchain. This function enables you to store your notes securely and immutably.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { setDoc } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@junobuild/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// TypeScript example from the documentation</span></span>
<span class="line"><span style="color:#FF79C6">await</span><span style="color:#50FA7B"> setDoc</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Example</span><span style="color:#F8F8F2">>({</span></span>
<span class="line"><span style="color:#F8F8F2">    collection</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">my_collection_key</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    doc</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        key</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">my_document_key</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        data</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> myExample</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><p>Since the documents in the collection are identified by a unique key, we create keys using <a href="https://github.com/ai/nanoid" rel="noopener noreferrer external">nanoid</a> - a tiny string ID generator for JavaScript.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#50FA7B;font-style:italic"> lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ts</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> setup</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#FF79C6">    import</span><span style="color:#F8F8F2"> { ref } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">vue</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">    import</span><span style="color:#F8F8F2"> { setDoc } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@junobuild/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">    import</span><span style="color:#F8F8F2"> { nanoid } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">nanoid</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> inputText </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> ref</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">""</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#50FA7B"> add</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> key </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> nanoid</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        await</span><span style="color:#50FA7B"> setDoc</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">            collection</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">notes</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">            doc</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">                key,</span></span>
<span class="line"><span style="color:#F8F8F2">                data</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">                    text</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> inputText.value</span></span>
<span class="line"><span style="color:#F8F8F2">                }</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">template</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">textarea</span><span style="color:#50FA7B;font-style:italic"> rows</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">5</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> placeholder</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Your diary entry</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> v-model</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">inputText</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">textarea</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">button</span><span style="color:#50FA7B;font-style:italic"> @click</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">add</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Add&#x3C;/</span><span style="color:#FF79C6">button</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">template</span><span style="color:#F8F8F2">></span></span></code></pre><p><em>Please note that for the sake of simplicity, the code snippets provided in this tutorial do not include proper error handling nor complex form handling.</em></p>
<hr />
<h3>Listing Documents</h3>
<p>In order to retrieve the collection of documents stored on the blockchain, we can utilize the library's <code>listDocs</code> function. This versatile function allows for the inclusion of various parameters to facilitate data filtering, ordering, or pagination.</p>
<p>For the purpose of this tutorial, we will keep the example minimalistic. Our objective is to simply list all user data when a component is mounted.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#50FA7B;font-style:italic"> lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ts</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> setup</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#FF79C6">    import</span><span style="color:#F8F8F2"> { listDocs } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@junobuild/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">    import</span><span style="color:#F8F8F2"> { onMounted, ref } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">vue</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> items </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> ref</span><span style="color:#F8F8F2">([]);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#50FA7B"> list</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> { items</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> data } </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> listDocs</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">            collection</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">notes</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        items.value </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> data;</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    onMounted</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> list</span><span style="color:#F8F8F2">());</span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">template</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#50FA7B;font-style:italic"> v-for</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">(item, index) in items</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">span</span><span style="color:#F8F8F2">> {{ index + 1 }} &#x3C;/</span><span style="color:#FF79C6">span</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">span</span><span style="color:#F8F8F2">>{{ item.data.text }}&#x3C;/</span><span style="color:#FF79C6">span</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">template</span><span style="color:#F8F8F2">></span></span></code></pre><hr />
<h3>Uploading File</h3>
<p>Storing data on the decentralized web can be a complex task. However, Juno is designed to simplify this process for app developers who require easy storage and retrieval of user-generated content, such as photos or files.</p>
<p>When it comes to handling documents, the first step is to create a collection by following the instructions provided in the <a href="https://juno.build/docs/build/storage#collections-and-rules" rel="noopener noreferrer external">documentation</a>. In this tutorial, we will focus on implementing image uploads, so the collection can be appropriately named as &quot;images.&quot;</p>
<p>To ensure uniqueness and proper identification of stored data, each file is assigned a unique file name and path. This is essential because the data is served on the web, and each piece of data should have a distinct URL.</p>
<p>To accomplish this, we can create a key using a combination of the user's unique ID in its textual representation and a timestamp for each uploaded file. By accessing the property we previously declared in a store, we can retrieve the corresponding user's key.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#50FA7B;font-style:italic"> lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ts</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> setup</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#FF79C6">    import</span><span style="color:#F8F8F2"> { ref } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">vue</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">    import</span><span style="color:#F8F8F2"> { useAuthStore } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@/stores/auth.store</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">    import</span><span style="color:#F8F8F2"> { storeToRefs } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">pinia</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">    import</span><span style="color:#F8F8F2"> { uploadFile } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@junobuild/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> file </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> ref</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">undefined</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> store </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useAuthStore</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { user } </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> storeToRefs</span><span style="color:#F8F8F2">(store);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#50FA7B"> setFile</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">f</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> (file.value </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> f);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#50FA7B"> upload</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#6272A4">        // Demo purpose therefore edge case not properly handled</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> ([</span><span style="color:#BD93F9">null</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">undefined</span><span style="color:#F8F8F2">].</span><span style="color:#50FA7B">includes</span><span style="color:#F8F8F2">(user.value)) {</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> filename </span><span style="color:#FF79C6">=</span><span style="color:#F1FA8C"> `</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">user.value.key</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">-</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">file.value.name</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> { downloadUrl } </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> uploadFile</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">            collection</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">images</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">            data</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> file.value,</span></span>
<span class="line"><span style="color:#F8F8F2">            filename</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Uploaded</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, downloadUrl);</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">template</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">input</span><span style="color:#50FA7B;font-style:italic"> type</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">file</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> @change</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">(event) => setFile(event.target.files?.[0])</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">button</span><span style="color:#50FA7B;font-style:italic"> @click</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">upload</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Upload&#x3C;/</span><span style="color:#FF79C6">button</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">template</span><span style="color:#F8F8F2">></span></span></code></pre><p>Once an asset is uploaded, a <code>downloadUrl</code> is returned which provides a direct HTTPS link to access the uploaded asset on the web.</p>
<hr />
<h3>Listing Assets</h3>
<p>To retrieve the collection of assets stored on the blockchain, we can utilize the <code>listAssets</code> function provided by the library. This function offers flexibility in terms of parameters, allowing us to filter, order, or paginate the files as needed.</p>
<p>Similar to the previous example with documents, we will keep this example minimalistic.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#50FA7B;font-style:italic"> lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ts</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> setup</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#FF79C6">    import</span><span style="color:#F8F8F2"> { listAssets } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@junobuild/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">    import</span><span style="color:#F8F8F2"> { onMounted, ref } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">vue</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> assets </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> ref</span><span style="color:#F8F8F2">([]);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#50FA7B"> list</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> { assets</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> images } </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> listAssets</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">            collection</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">images</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        assets.value </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> images;</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    onMounted</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> list</span><span style="color:#F8F8F2">());</span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">template</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">img</span><span style="color:#50FA7B;font-style:italic"> loading</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">lazy</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> :src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">asset.downloadUrl</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> v-for</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">asset in assets</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">template</span><span style="color:#F8F8F2">></span></span></code></pre><hr />
<h3>Deployment 🚀</h3>
<p>After you have developed and built your application, the next step is to deploy it on the blockchain. To do this, you need to install the Juno <a href="https://juno.build/docs/miscellaneous/cli" rel="noopener noreferrer external">command line interface (CLI)</a> by executing the following command in your terminal:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> i</span><span style="color:#BD93F9"> -g</span><span style="color:#F1FA8C"> @junobuild/cli</span></span></code></pre><p>After the installation process is finished, you can gain access to your satellite by following the instructions in the <a href="https://juno.build/docs/miscellaneous/cli#login" rel="noopener noreferrer external">documentation</a> and logging in from the terminal. This will enable your machine to control your satellite.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">juno</span><span style="color:#F1FA8C"> login</span></span></code></pre><p>Finally, you can deploy your project using the following command:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">juno</span><span style="color:#F1FA8C"> deploy</span></span></code></pre><p>Congratulations! Your Vue dapp is now live and fully powered by the blockchain 🎉.</p>
<hr />
<h2>Resources</h2>
<ul>
<li>Juno documentation and getting started: <a href="https://juno.build/docs/intro" rel="noopener noreferrer external">https://juno.build/docs/intro</a></li>
<li>The source code of this tutorial is available in our <a href="https://github.com/junobuild/examples/tree/main/vue/diary" rel="noopener noreferrer external">GitHub Repository</a>.</li>
</ul>
<p>To infinity and beyond<br />
David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Translate a Svelte app (i18n) without external dependencies]]></title>
          <description><![CDATA[Adding internationalization support to a Svelte application: a step-by-step guide.]]></description>
          <link>https://daviddalbusco.com/blog/translate-a-svelte-app-i18n/</link>
          <pubDate>Thu, 18 May 2023 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*bXhlRgLdEKKomTCR32Q4ZA.jpeg" alt="nareeta-martin-vf1ycolhmpg-unsplash.jpg" / loading="lazy"></p>
<p><em>Photo by <a href="https://medium.com/r/?url=https%3A%2F%2Funsplash.com%2Fpt-br%2F%40splashabout%3Futm_source%3Dunsplash%26utm_medium%3Dreferral%26utm_content%3DcreditCopyText" rel="noopener noreferrer external">Nareeta Martin</a> on <a href="https://medium.com/r/?url=https%3A%2F%2Funsplash.com%2Fphotos%2FvF1YCoLHMpg%3Futm_source%3Dunsplash%26utm_medium%3Dreferral%26utm_content%3DcreditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<hr />
<p>As a native French speaker living in the Swiss-German part of Switzerland, it goes without saying that translations, or more precisely, enabling users to switch languages in their apps, is a topic I am well-acquainted with. I consistently implement this functionality at the beginning of every project, regardless of whether the app will be fully translated or not.</p>
<p>Over the course of building several Svelte applications, I have discovered a recurring pattern that I consistently employ. This pattern, which I have successfully used in previous projects with different frameworks, revolves around the utilization of a lightweight store for managing translation keys, all without relying on any third-party dependencies.</p>
<p>In this tutorial, I’ll share this solution and provide practical features like generating TypeScript definitions and translation utilities. These tools will streamline your workflow.</p>
<hr />
<h2>Important considerations: SEO limitations</h2>
<p>This article covers a <strong>client-side solution</strong> for translations, which may have limitations in terms of search engine optimization (SEO). It does not address multi-language websites delivered through separate URLs or domains. While it is possible to extend the solution to accommodate such scenarios, it falls outside the scope of this tutorial’s objectives.</p>
<hr />
<h2>Language definition</h2>
<p>When adding translations to an existing Svelte application, the first step is to define the list of supported languages.</p>
<p>To accomplish this, create a <code>languages.d.ts</code> TypeScript definition file that enumerates the supported languages. For example, you can include English and Chinese as supported languages.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">type</span><span style="color:#8BE9FD;font-style:italic"> Languages</span><span style="color:#FF79C6"> =</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">en</span><span style="color:#E9F284">"</span><span style="color:#FF79C6"> |</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">zh-cn</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span></code></pre><hr />
<h2>Typing support</h2>
<p>To enable typing support in TypeScript for the translation keys used in our application, we can create an <code>interface</code>. In this blog post, we will explore an automated approach for generating these interfaces. However, for now, let's manually create an <code>i18n.d.ts</code> file that defines two translated keys, namely <code>yes</code> and <code>no</code>. This will establish a foundation for managing typings for translations in our Svelte application.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">interface</span><span style="color:#8BE9FD;font-style:italic"> I18nCore</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    yes</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    no</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">interface</span><span style="color:#8BE9FD;font-style:italic"> I18n</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    lang</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Languages</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    core</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> I18nCore</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>One approach to implementing these languages is to define them directly in the code.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> en</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> I18n</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    lang</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">en</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    core</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        yes</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">yes</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        no</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">no</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> zhCn</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> I18n</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    lang</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">en</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    core</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        yes</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">是</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        no</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">否</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Personally, I find using JSON files to store translations more convenient than embedding them directly in code. This approach has proven to be accessible to translators and non-technical individuals I have collaborated with in the past.</p>
<p>For instance, I use an <code>en.json</code> file to store English translations, a <code>zh-cn.json</code> file for Chinese translations, and so on.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#8BE9FE">  "</span><span style="color:#8BE9FD">core</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">yes</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Yes</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">no</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">No</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#8BE9FE"> "</span><span style="color:#8BE9FD">core</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FE">     "</span><span style="color:#8BE9FD">yes</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">是</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">  "</span><span style="color:#8BE9FD">no</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">否</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2"> }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h2>Lazy loading</h2>
<p>Although it may not be critical if your app only has a small number of translations, loading all translations for every language at app startup can become problematic. To address this, I prefer to load only the default language initially and lazily load the others as needed.</p>
<p>In this tutorial, we will use English as the default language. To obtain the list of translations, we can utilize a statically built import.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> en </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">$lib/i18n/en.json</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> enI18n</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> I18n</span><span style="color:#FF79C6"> =></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        lang</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">en</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#FF79C6">        ...</span><span style="color:#F8F8F2">en</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> I18n</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Given that Chinese is an option, we can implement lazy loading for it using an asynchronous import. This import will be executed at runtime only if Chinese translations are needed, ensuring efficient resource utilization and improved performance.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> zhCnI18n</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">I18n</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        lang</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">zh-cn</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#FF79C6">        ...</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">await</span><span style="color:#FF79C6"> import</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`../i18n/zh-cn.json`</span><span style="color:#F8F8F2">))</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>By implementing lazy loading, only users who request a specific language will need to download the corresponding JSON file, reducing unnecessary data transfer.</p>
<p>To initialize our project, we can create a single function that is called at boot time to handle translation initialization. This function will set up the necessary configurations and prepare the application for seamless translation integration.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> loadLanguage</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">lang</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Languages</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">I18n</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    switch</span><span style="color:#F8F8F2"> (lang) {</span></span>
<span class="line"><span style="color:#FF79C6">        case</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">zh-cn</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">:</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#50FA7B"> zhCnI18n</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">        default</span><span style="color:#F8F8F2">:</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">resolve</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">enI18n</span><span style="color:#F8F8F2">());</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><hr />
<h2>Local storage</h2>
<p>Considering the goal of supporting multiple languages in our application, it is crucial to deliver the content in the user’s preferred language whenever they visit the site.</p>
<p>To achieve this, a straightforward approach is to save the user’s language choice in the local storage. Since unforeseen circumstances can arise and server-side rendering is not a concern in this tutorial, we can encapsulate the storage logic within error-ignoring functions. This ensures that even in error scenarios, our application can still compile and operate smoothly.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { browser } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">$app/environment</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> setLocalStorageItem</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">key</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">value</span><span style="color:#F8F8F2"> }</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { key</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">; value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        localStorage.</span><span style="color:#50FA7B">setItem</span><span style="color:#F8F8F2">(key, value);</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">err</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> unknown</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#6272A4">        // We use the local storage for the operational part of the app but, fallback to english if necessary</span></span>
<span class="line"><span style="color:#F8F8F2">        console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(err);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> getLocalStorageLang</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Languages</span><span style="color:#FF79C6"> =></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> { lang }</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Storage</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> browser </span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2"> localStorage </span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> ({ lang</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">en</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> } </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> unknown</span><span style="color:#FF79C6"> as</span><span style="color:#8BE9FD;font-style:italic"> Storage</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> lang;</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">err</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> unknown</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#6272A4">        // We use the local storage for the operational part of the app but, fallback to english if necessary</span></span>
<span class="line"><span style="color:#F8F8F2">        console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(err);</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">en</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><hr />
<h2>Store</h2>
<p>With the ability to load languages and save the user’s language choice, we can now proceed to develop the translation store.</p>
<p>The translation store will not only store the translations but also expose two key functions: one for initialization purposes and another for switching between languages. These functions will provide the necessary functionality to set up the translations and allow users to seamlessly switch between different language options.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> interface</span><span style="color:#8BE9FD;font-style:italic"> InitI18nStore</span><span style="color:#FF79C6"> extends</span><span style="color:#8BE9FD;font-style:italic"> Readable</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">I18n</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#50FA7B">    init</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">void</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#50FA7B">    switchLang</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">lang</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Languages</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">void</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>The implementation of the translation store extends the above interface and involves the following tasks:</p>
<ul>
<li><p>Create a Svelte <code>writable</code> store that is initialized with the default value of English.</p></li>
<li><p>Expose the store's subscriber.</p></li>
<li><p>Implement one function to populate the store with translations based on the selected language.</p></li>
<li><p>And another function to persist the language choice in the storage.</p></li>
</ul>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> initI18n</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> I18nStore</span><span style="color:#FF79C6"> =></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { subscribe, set } </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> writable</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">I18n</span><span style="color:#F8F8F2">>({</span></span>
<span class="line"><span style="color:#F8F8F2">        lang</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">en</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#FF79C6">        ...</span><span style="color:#F8F8F2">en</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#50FA7B"> save</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">lang</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Languages</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> setLocalStorageItem</span><span style="color:#F8F8F2">({ key</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">lang</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, value</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> lang });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        subscribe,</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">        init</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">            const</span><span style="color:#F8F8F2"> lang</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Languages</span><span style="color:#FF79C6"> =</span><span style="color:#50FA7B"> getLocalStorageLang</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">            if</span><span style="color:#F8F8F2"> (lang </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">en</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#50FA7B">                save</span><span style="color:#F8F8F2">(lang);</span></span>
<span class="line"><span style="color:#6272A4">                // No need to reload the store, English is already the default</span></span>
<span class="line"><span style="color:#FF79C6">                return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">            const</span><span style="color:#F8F8F2"> bundle</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> I18n</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> loadLanguage</span><span style="color:#F8F8F2">(lang);</span></span>
<span class="line"><span style="color:#50FA7B">            set</span><span style="color:#F8F8F2">(bundle);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">            save</span><span style="color:#F8F8F2">(lang);</span></span>
<span class="line"><span style="color:#F8F8F2">        },</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">        switchLang</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">lang</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Languages</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">            const</span><span style="color:#F8F8F2"> bundle</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> I18n</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> loadLanguage</span><span style="color:#F8F8F2">(lang);</span></span>
<span class="line"><span style="color:#50FA7B">            set</span><span style="color:#F8F8F2">(bundle);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">            save</span><span style="color:#F8F8F2">(lang);</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> i18n </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> initI18n</span><span style="color:#F8F8F2">();</span></span></code></pre><hr />
<h2>Initialization</h2>
<p>The store and loading of translations can be executed at any point during the application startup process. Personally, I prefer utilizing the <code>await</code> tag in Svelte to initialize such quick loaders at the root of my main <code>+layout.ts</code> file. This approach allows me to display a brief spinner while the translations are loading. However, the loading process is often so quick that the spinner may not even be visible. Additionally, this ensures that the UI is not rendered in English before being switched to the user's preferred language, especially if they had previously made a different language selection.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2">script lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ts</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">></span></span>
<span class="line"><span style="color:#FF79C6">    import</span><span style="color:#F8F8F2"> { i18n } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">$lib/stores/i18n.store</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">    import</span><span style="color:#F8F8F2"> MySpinner </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">$lib/components/ui/MySpinner.svelte</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#50FA7B"> init</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> i18n.</span><span style="color:#50FA7B">init</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">&#x3C;/</span><span style="color:#F8F8F2">script</span><span style="color:#FF79C6">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">{#</span><span style="color:#FF79C6">await</span><span style="color:#50FA7B"> init</span><span style="color:#F8F8F2">()}</span></span>
<span class="line"><span style="color:#FF79C6">    &#x3C;</span><span style="color:#F8F8F2">MySpinner </span><span style="color:#FF79C6">/></span></span>
<span class="line"><span style="color:#F8F8F2">{:then _}</span></span>
<span class="line"><span style="color:#FF79C6">    &#x3C;</span><span style="color:#F8F8F2">slot </span><span style="color:#FF79C6">/></span></span>
<span class="line"><span style="color:#F8F8F2">{</span><span style="color:#FF79C6">/await</span><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h2>Usage</h2>
<p>The beauty of this solution lies in its simplicity, as it leverages Svelte stores, which you are likely already using in your application. There is no need to learn anything new to utilize them.</p>
<p>By auto-subscribing to translation keys, the text will be dynamically rendered. This means that whenever the user switches the language, the subscriber will be re-rendered, and the newly selected language will be automatically applied. This enables seamless and dynamic language switching within the application.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2">script lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ts</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">></span></span>
<span class="line"><span style="color:#FF79C6">  import</span><span style="color:#F8F8F2"> {i18n} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">$lib/stores/i18n.store</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">&#x3C;/</span><span style="color:#F8F8F2">script</span><span style="color:#FF79C6">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">p</span><span style="color:#F8F8F2">>{$i18n.core.yes}</span><span style="color:#FF79C6">&#x3C;/</span><span style="color:#F8F8F2">p</span><span style="color:#FF79C6">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">p</span><span style="color:#F8F8F2">>{$i18n.core.no}</span><span style="color:#FF79C6">&#x3C;/</span><span style="color:#F8F8F2">p</span><span style="color:#FF79C6">></span></span></code></pre><p>Similarly, the language can be switched by calling the corresponding function implemented in our translation store.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2">script lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ts</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">></span></span>
<span class="line"><span style="color:#FF79C6">  import</span><span style="color:#F8F8F2"> {i18n} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">$lib/stores/i18n.store</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#50FA7B"> switchChinese</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> i18n.</span><span style="color:#50FA7B">switchLang</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">zh-cn</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">&#x3C;/</span><span style="color:#F8F8F2">script</span><span style="color:#FF79C6">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2">button on:click</span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2">{switchChinese}</span><span style="color:#FF79C6">></span><span style="color:#F8F8F2">Chinese</span><span style="color:#FF79C6">&#x3C;/</span><span style="color:#F8F8F2">button</span><span style="color:#FF79C6">></span></span></code></pre><hr />
<h2>Generate TypeScript definition automatically</h2>
<p>To automatically generate the TypeScript definitions for our translation keys, we can create a Node.js script at the root of our project. This script will iterate through the default language, which is English in this case, and generate the necessary definitions.</p>
<p>It’s important to note that for the sake of simplicity, the script only follows a single level down. This means that it will generate types for keys at the current level, but not for nested keys like <code>$i18n.level.sublevel.key</code>.</p>
<p>By running this script, we can conveniently generate the TypeScript definitions required for our translations, enabling type-checking and improved development experience.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">#!/usr/bin/env node</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { writeFileSync } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">fs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> PATH_FROM_ROOT </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./src/frontend/src</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> PATH_TO_EN_JSON </span><span style="color:#FF79C6">=</span><span style="color:#F1FA8C"> `</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">PATH_FROM_ROOT</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">/lib/i18n/en.json`</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> PATH_TO_OUTPUT </span><span style="color:#FF79C6">=</span><span style="color:#F1FA8C"> `</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">PATH_FROM_ROOT</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">/lib/types/i18n.d.ts`</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">/**</span></span>
<span class="line"><span style="color:#6272A4"> * Generate the TypeScript interfaces from the english translation file.</span></span>
<span class="line"><span style="color:#6272A4"> *</span></span>
<span class="line"><span style="color:#6272A4"> * Note: only supports "a one child depth" in the data structure.</span></span>
<span class="line"><span style="color:#6272A4"> */</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> generateTypes</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> en </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> import</span><span style="color:#F8F8F2">(PATH_TO_EN_JSON, { assert</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { type</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">json</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> } });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> data </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> Object.</span><span style="color:#50FA7B">keys</span><span style="color:#F8F8F2">(en.default).</span><span style="color:#50FA7B">map</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">key</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> properties </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> Object.</span><span style="color:#50FA7B">keys</span><span style="color:#F8F8F2">(en.default[key]).</span><span style="color:#50FA7B">map</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">prop</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F1FA8C"> `</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">prop</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">: string;`</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">            key,</span></span>
<span class="line"><span style="color:#F8F8F2">            name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> `I18n</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">key.</span><span style="color:#50FA7B">charAt</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">).</span><span style="color:#50FA7B">toUpperCase</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">}${</span><span style="color:#F8F8F2">key.</span><span style="color:#50FA7B">slice</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">            properties</span></span>
<span class="line"><span style="color:#F8F8F2">        };</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> lang </span><span style="color:#FF79C6">=</span><span style="color:#F1FA8C"> `lang: Languages;`</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> main </span><span style="color:#FF79C6">=</span><span style="color:#F1FA8C"> `</span><span style="color:#FF79C6">\n\n</span><span style="color:#F1FA8C">interface I18n {</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">lang</span><span style="color:#FF79C6">}${</span><span style="color:#F8F8F2">data.</span><span style="color:#50FA7B">map</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">(</span><span style="color:#FFB86C;font-style:italic">i</span><span style="color:#F1FA8C">) </span><span style="color:#FF79C6">=></span><span style="color:#F1FA8C"> `</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">i.key</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">: </span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">i.name</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">;`</span><span style="color:#F8F8F2">).</span><span style="color:#50FA7B">join</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">""</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">}`</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> interfaces </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> data.</span><span style="color:#50FA7B">map</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">i</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F1FA8C"> `</span><span style="color:#FF79C6">\n\n</span><span style="color:#F1FA8C">interface </span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">i.name</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C"> {</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">i.properties.</span><span style="color:#50FA7B">join</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">""</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">}`</span><span style="color:#F8F8F2">).</span><span style="color:#50FA7B">join</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">""</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> comment </span><span style="color:#FF79C6">=</span><span style="color:#F1FA8C"> `/**</span><span style="color:#FF79C6">\n</span><span style="color:#F1FA8C">* Auto-generated definitions file ("npm run i18n")</span><span style="color:#FF79C6">\n</span><span style="color:#F1FA8C">*/`</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    writeFileSync</span><span style="color:#F8F8F2">(PATH_TO_OUTPUT, </span><span style="color:#F1FA8C">`</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">comment</span><span style="color:#FF79C6">}${</span><span style="color:#F8F8F2">interfaces</span><span style="color:#FF79C6">}${</span><span style="color:#F8F8F2">main</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">await</span><span style="color:#50FA7B"> generateTypes</span><span style="color:#F8F8F2">();</span></span></code></pre><p>To simplify the process of running the parser, we can add it to the list of scripts in our <code>package.json</code> file. Additionally, if you are using a code formatter like <code>prettier</code>, we can format the generated code afterwards to ensure it adheres to our desired code style and patterns. This ensures consistency and readability in the generated code.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#E9F284">  "</span><span style="color:#F1FA8C">scripts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">: {</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">i18n</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">node i18n.mjs &#x26;&#x26; prettier --write ./src/frontend/src/lib/types/i18n.d.ts</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>By running the command <code>npm run i18n</code> in a terminal, the types will be automatically generated. It is recommended to run this command each time new translation keys are added to ensure the generated code is up to date. For safety reasons, it is also advisable to run the command before a production build to ensure that the generated code incorporates the most recent entries. This guarantees that the application uses the latest translations during the build process.</p>
<hr />
<h2>Replace placeholders</h2>
<p>Using static texts is convenient, but frequently we need to replace placeholders in sentences to incorporate dynamic values. This allows us to present content that adapts based on user input or other dynamic factors.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">core</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">problems</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">I got {0} problems but a witch ain't {1}</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>This can be achieved with the help of a small utility that iterates through a value and replaces matching texts. There are multiple ways to implement this functionality, and here is a simple approach to consider.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> i18nFormat</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#FFB86C;font-style:italic">    text</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#FFB86C;font-style:italic">    params</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { placeholder</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">; value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2"> }[]</span></span>
<span class="line"><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    params.</span><span style="color:#50FA7B">forEach</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">param</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> split </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> text.</span><span style="color:#50FA7B">split</span><span style="color:#F8F8F2">(param.placeholder);</span></span>
<span class="line"><span style="color:#F8F8F2">        text </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> split[</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">] </span><span style="color:#FF79C6">+</span><span style="color:#F8F8F2"> param.value </span><span style="color:#FF79C6">+</span><span style="color:#F8F8F2"> (split.length </span><span style="color:#FF79C6">></span><span style="color:#BD93F9"> 1</span><span style="color:#FF79C6"> ?</span><span style="color:#F8F8F2"> split[</span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">] </span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> ""</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> text;</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>A function can be created to replace the selected keys in the input text with the provided parameters.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2">script lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ts</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">></span></span>
<span class="line"><span style="color:#FF79C6">  import</span><span style="color:#F8F8F2"> {i18nFormat} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">$lib/utils/i18n.utils</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">&#x3C;/</span><span style="color:#F8F8F2">script</span><span style="color:#FF79C6">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">p</span><span style="color:#F8F8F2">>{</span><span style="color:#50FA7B">i18nFormat</span><span style="color:#F8F8F2">($i18n.core.problems, [ { placeholder: </span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">{0}</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, value: </span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">99</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2"> }, { placeholder: </span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">{1}</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, value: </span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">1</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2"> } ])}</span><span style="color:#FF79C6">&#x3C;/</span><span style="color:#F8F8F2">p</span><span style="color:#FF79C6">></span></span></code></pre><p>Note that in this example, <code>{0}</code> and <code>{1}</code> were used as placeholders, but you can use any unique identifiers as placeholders.</p>
<hr />
<h2>HTML tags</h2>
<p>Sometimes, we may also need to apply styling within our translations. Since translations are just strings, we can include HTML tags within the text and utilize the <code>@html</code> tag in Svelte to render them properly. This allows us to incorporate styling elements and formatting directly within the translated text.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">core</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">bold</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">This is &#x3C;strong>bold&#x3C;/strong>.</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p><em>Note: It's important to exercise caution when using the @html tag, as it can pose security risks. Make sure to use it only when necessary and follow best practices to mitigate potential vulnerabilities. Implementing a Content Security Policy (CSP) is highly recommended, and it's advisable to purify the HTML content before injecting it into the DOM. These precautions help ensure the safety and integrity of your application when using the <code>@html</code> tag.</em></p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2">script lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ts</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">></span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {i18n} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">$lib/stores/i18n.store</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">&#x3C;/</span><span style="color:#F8F8F2">script</span><span style="color:#FF79C6">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">p</span><span style="color:#F8F8F2">>{@html $i18n.core.bold}</span><span style="color:#FF79C6">&#x3C;/</span><span style="color:#F8F8F2">p</span><span style="color:#FF79C6">></span></span></code></pre><h2>Source code</h2>
<p>The source code for this tutorial can be found in the <a href="https://github.com/junobuild/juno" rel="noopener noreferrer external">GitHub repository</a> of <a href="https://juno.build/" rel="noopener noreferrer external">Juno</a>, an open-source blockchainless platform designed for developers who want to build Web3 applications with the simplicity of Web2.</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Fake video capture with Playwright]]></title>
          <description><![CDATA[How to mock the camera for an End-to-End test that requires a video stream capture.]]></description>
          <link>https://daviddalbusco.com/blog/fake-video-capture-with-playwright/</link>
          <pubDate>Sun, 30 Apr 2023 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://images.unsplash.com/photo-1579724186435-56a4bd84ab31?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwzMDkyMzV8MHwxfHNlYXJjaHwyfHxsZWdvJTIwdG95JTIwY2FtZXJhfGVufDB8fHx8MTY4Mjg2MzYxMQ&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080" alt="lego toy camera" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@xavi_cabrera?utm_source=Papyrs&amp;utm_medium=referral" rel="noopener noreferrer external">Xavi Cabrera</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<hr />
<p>We recently added a QR code reader feature to NNS-dapp, a decentralized application that enables interaction with the Internet Computer’s <a href="https://internetcomputer.org/nns" rel="noopener noreferrer external">Network Nervous System</a>. To ensure its maintainability, we created an end-to-end test using <a href="https://playwright.dev/" rel="noopener noreferrer external">Playwright</a> to simulate video capture for automation purposes. As we had to gather information from various sources to make this happen, I decided to share our insights in a single blog post to assist others who may encounter similar challenges.</p>
<hr />
<h2>Demo</h2>
<p>This is how one of our tests operates: it opens a modal that utilizes the QR code reader to read and decode the video stream. The decoded result is then parsed into a textarea and ultimately validated using pixel-based screenshot comparison.</p>
<p><img src="https://6zvwc-sqaaa-aaaal-aalma-cai.raw.icp0.io/images/qrcodereader.gif?token=R1W6tS9Zj3eKo-bkHI9O6" alt="qrcodereader.gif" / loading="lazy"></p>
<hr />
<h2>Limitations</h2>
<p>Before proceeding further, I would like to highlight the various technical limitations that are required to set up such a test. Please note that as of writing this article on April 30th, 2023, Playwright may have evolved since then. Therefore, it is always advisable to double-check the documentation first.</p>
<hr />
<h3>Chrome only</h3>
<p>It is not possible to mock the camera in Safari. As documented in this GitHub <a href="https://github.com/microsoft/playwright/issues/4532" rel="noopener noreferrer external">issue</a>, it is possible to mock the video capture in Firefox, but it is not possible to provide a specific video file. Therefore, it is safe to assume that mocking the camera, the way we intended to do with Playwright, only works in Google Chrome.</p>
<hr />
<h3>YUV4MPEG2</h3>
<p>Based on my experimentation, it appears that the only video format that can be used by Chrome to feed a test file to <code>getUserMedia()</code> instead of live camera input is the <a href="https://wiki.multimedia.cx/index.php/YUV4MPEG2" rel="noopener noreferrer external">.y4m</a> file format. This file format is designed to hold uncompressed frames, so it is also worth noting that the video can be quite large in size.</p>
<hr />
<h2>Playwright configuration</h2>
<p>There is nothing in particular to set in the Playwright configuration unless you decide to use Chrome as the engine for your tests globally. In our case, we have done so in our open-source UI kit library called <a href="https://github.com/dfinity/gix-components" rel="noopener noreferrer external">gix-components</a> which is built with <a href="https://kit.svelte.dev/" rel="noopener noreferrer external">SvelteKit</a>, and this is how we have configured it in our <code>playwright.config.ts</code> file.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#FF79C6"> type</span><span style="color:#F8F8F2"> { PlaywrightTestConfig } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@playwright/test</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { devices } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@playwright/test</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> config</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> PlaywrightTestConfig</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    webServer</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        command</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">npm run build &#x26;&#x26; npm run preview</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        port</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 4173</span></span>
<span class="line"><span style="color:#F8F8F2">    },</span></span>
<span class="line"><span style="color:#F8F8F2">    testDir</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">e2e</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    testMatch</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">**/*.e2e.ts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">    use</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        testIdAttribute</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">data-tid</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        trace</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">on</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    },</span></span>
<span class="line"><span style="color:#F8F8F2">    projects</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#F8F8F2">        {</span></span>
<span class="line"><span style="color:#F8F8F2">            name</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Google Chrome</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">            use</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { </span><span style="color:#FF79C6">...</span><span style="color:#F8F8F2">devices[</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Desktop Chrome</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">], channel</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">chrome</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> }</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    ]</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> config;</span></span></code></pre><hr />
<h2>Test</h2>
<p>To fake a video capture for the End-to-End test, you need to pass three arguments to Chromium during launch.</p>
<ul>
<li><code>--use-fake-ui-for-media-stream</code>: eliminates the need to grant camera and microphone permissions during the test</li>
<li><code>--use-fake-device-for-media-stream</code>: injects a test pattern into <code>getUserMedia()</code> instead of using live camera input</li>
<li><code>--use-file-for-fake-video-capture=./your-file.y4m</code>: the path to the video file that should be used to simulate the camera video stream</li>
</ul>
<p>Other than these arguments, there is no need to set anything else. Here is an example of a simple test that uses this approach.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">test</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Read QR code with camera</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> browser </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> chromium.</span><span style="color:#50FA7B">launch</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        args</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#E9F284">            "</span><span style="color:#F1FA8C">--use-fake-ui-for-media-stream</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">            "</span><span style="color:#F1FA8C">--use-fake-device-for-media-stream</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">            "</span><span style="color:#F1FA8C">--use-file-for-fake-video-capture=./samples/qrcode.y4m</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">        ]</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> context </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> browser.</span><span style="color:#50FA7B">newContext</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        permissions</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">camera</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">]</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> page </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> context.</span><span style="color:#50FA7B">newPage</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // Navigate</span></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#F8F8F2"> page.</span><span style="color:#50FA7B">goto</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // e.g. open the element that uses the QR code reader</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> modal </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> page.</span><span style="color:#50FA7B">getByTestId</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">qr-code-modal</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#F8F8F2"> modal.</span><span style="color:#50FA7B">click</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // Optimistically wait process to end</span></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#F8F8F2"> page.</span><span style="color:#50FA7B">waitForTimeout</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">2000</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#50FA7B"> expect</span><span style="color:#F8F8F2">(page).</span><span style="color:#50FA7B">toHaveScreenshot</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><p>It’s worth noting that explicitly requesting permission to use the camera access using the <a href="https://playwright.dev/docs/api/class-browsercontext" rel="noopener noreferrer external">BrowserContext</a> of Playwright is not absolutely necessary as we are already granting such permission by using a launch argument. One can never be too sure isn’t it 😉?</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Develop An Angular App On Blockchain]]></title>
          <description><![CDATA[How to create a Web3 decentralized application using Angular and Juno.]]></description>
          <link>https://daviddalbusco.com/blog/develop-an-angular-app-on-blockchain/</link>
          <pubDate>Fri, 21 Apr 2023 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://images.unsplash.com/photo-1514428631868-a400b561ff44?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwzMDkyMzV8MHwxfHNlYXJjaHwyfHxjb21wdXRlciUyMGFuZ3VsYXJ8ZW58MHx8fHwxNjgyMDgwMTI3&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080" alt="Macbook Pro" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/fr/@maxcodes?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Maxwell Nelson</a> on <a href="https://unsplash.com/fr/photos/taiuG8CPKAQ?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<hr />
<h2>Introduction</h2>
<p>There are various Web3 development solutions with unique advantages and limitations. If you are a frontend JavaScript developer seeking to build on the decentralized web, you may find it challenging to navigate. Fortunately, <a href="https://juno.build" rel="noopener noreferrer external">Juno</a> provides a unique approach that combines Web3 power with the ease and familiarity of Web2 development.</p>
<p>After exploring how to combine React and Juno to develop a dApp in a previous <a href="https://juno.build/blog/build-a-web3-app-with-react-js" rel="noopener noreferrer external">blog post</a>, we are now going to take a look at how to develop an Angular app on blockchain.</p>
<p>So, let’s dive in and discover how you can build powerful and user-friendly decentralized applications with Angular!</p>
<hr />
<h2>How Juno Works</h2>
<p>If you’re not familiar with Juno, it’s an open-source Blockchain-as-a-Service platform that enables you to build decentralized applications with ease. Think of it as a serverless platform, like Google Firebase or AWS Amplify, but with the added benefits of blockchain technology. With Juno, everything runs on the blockchain, providing a fully decentralized and secure infrastructure for your applications.</p>
<p>Juno leverages the <a href="https://internetcomputer.org/" rel="noopener noreferrer external">Internet Computer</a> blockchain network and infrastructure to launch a “Satellite” for each app you build, essentially a powerful smart contract that contains your entire app. From the assets provided on the web, such as JavaScript, HTML, and image files, to its state saved in a super simple database, file storage, and authentication, each Satellite is controlled solely by you and contains everything it needs to run smoothly.</p>
<hr />
<h2>Build Your First Dapp</h2>
<p>Let’s start building our first decentralized application, or “dapp” for short. In this example, we’ll be creating a note-taking app that enables users to store and retrieve data entries, as well as upload files.</p>
<hr />
<h3>Initialization</h3>
<p>Before you can integrate Juno into your Angular app, you’ll need to create a satellite. This process is explained in detail in the <a href="https://juno.build/docs/add-juno-to-an-app/create-a-satellite" rel="noopener noreferrer external">documentation</a>.</p>
<p>Moreover, you also need to install the SDK.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> i</span><span style="color:#F1FA8C"> @junobuild/core</span></span></code></pre><p>After completing both of these steps, you can initialize Juno with your satellite ID in the main component of your Angular app. This will configure the library to communicate with your smart contract.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Component, OnInit } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { initJuno } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@junobuild/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    selector</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">app-root</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    templateUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./app.component.html</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    styleUrls</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">./app.component.scss</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">]</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> AppComponent</span><span style="color:#FF79C6"> implements</span><span style="color:#8BE9FD;font-style:italic"> OnInit</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    async</span><span style="color:#50FA7B"> ngOnInit</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        await</span><span style="color:#50FA7B"> initJuno</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">            satelliteId</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">pycrs-xiaaa-aaaal-ab6la-cai</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>That’s it for the configuration! Your app is now ready for Web3! 😎</p>
<hr />
<h3>Authentication</h3>
<p>To enable secure and <strong>anonymous</strong> user identification, users will need to sign in and sign out. You can bind the corresponding functions to call-to-action buttons anywhere in your app.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Component } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { signIn, signOut } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@junobuild/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    selector</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">app-demo</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    template</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> `&#x3C;button (click)="signIn()">Sign-in&#x3C;/button></span></span>
<span class="line"><span style="color:#F1FA8C">        &#x3C;button (click)="signOut()">Sign-out&#x3C;/button>`</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    standalone</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> DemoComponent</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    readonly</span><span style="color:#F8F8F2"> signOut </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> signOut;</span></span>
<span class="line"><span style="color:#FF79C6">    readonly</span><span style="color:#F8F8F2"> signIn </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> signIn;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>To integrate tightly with other services, the library and satellite automatically create a new entry in your smart contract when a user successfully signs in. This enables the library to check permissions on any exchange of data.</p>
<p>To observe this entry and, consequently, understand the user’s state, Juno offers an observable function called <code>authSubscribe()</code>. You can use this function as many times as needed, but it’s convenient to create a service that provides the information. This way, you can derive RxJS <code>Observable</code> to propagate the user.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Injectable } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { authSubscribe, User } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@junobuild/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { map, Observable } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">rxjs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Injectable</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    providedIn</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">root</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> AuthService</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    readonly</span><span style="color:#F8F8F2"> user$</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Observable</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">User</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Observable</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">observer</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span></span>
<span class="line"><span style="color:#50FA7B">        authSubscribe</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">user</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> observer.</span><span style="color:#50FA7B">next</span><span style="color:#F8F8F2">(user))</span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    readonly</span><span style="color:#F8F8F2"> signedIn$</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Observable</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">boolean</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.user$.</span><span style="color:#50FA7B">pipe</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">map</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">user</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> user </span><span style="color:#FF79C6">!==</span><span style="color:#BD93F9"> null</span><span style="color:#F8F8F2">));</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Juno’s library is designed to be framework-agnostic and currently doesn’t include any framework-specific code. However, we encourage contributions from the community, and if you’re interested in providing Angular bindings, we welcome your contributions to the project! 💪</p>
<hr />
<h3>Storing Documents</h3>
<p>Juno offers a feature called “Datastore” for storing data on the blockchain. A Datastore consists of a list of collections that hold your documents, each identified by a key that you define.</p>
<p>In this tutorial, we aim to store notes, so you’ll need to create a collection following the instructions in the <a href="https://juno.build/docs/build/datastore#collections-and-rules" rel="noopener noreferrer external">documentation</a> and name it accordingly (e.g., “notes”).</p>
<p>After setting up your app and creating the collection, you can use the <code>setDoc</code> function provided by the library to persist data on the blockchain.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { setDoc } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@junobuild/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// TypeScript example from the documentation</span></span>
<span class="line"><span style="color:#FF79C6">await</span><span style="color:#50FA7B"> setDoc</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Example</span><span style="color:#F8F8F2">>({</span></span>
<span class="line"><span style="color:#F8F8F2">    collection</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">my_collection_key</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    doc</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        key</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">my_document_key</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        data</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> myExample</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><p>Since the documents in the collection are identified by a unique key, we create keys using <a href="https://github.com/ai/nanoid" rel="noopener noreferrer external">nanoid</a> — a tiny string ID generator for JavaScript.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Component } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { FormBuilder, ReactiveFormsModule } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@angular/forms</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { setDoc } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@junobuild/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { nanoid } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">nanoid</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Entry } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">../../types/entry</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    selector</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">app-input</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    template</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> ` &#x3C;form (ngSubmit)="onSubmit()" [formGroup]="entryForm"></span></span>
<span class="line"><span style="color:#F1FA8C">        &#x3C;textarea formControlName="entry">&#x3C;/textarea></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F1FA8C">        &#x3C;button [disabled]="entryForm.disabled">Submit&#x3C;/button></span></span>
<span class="line"><span style="color:#F1FA8C">    &#x3C;/form>`</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    standalone</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    imports</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#50FA7B;font-style:italic">ReactiveFormsModule</span><span style="color:#F8F8F2">]</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> InputComponent</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    entryForm </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.formBuilder.</span><span style="color:#50FA7B">group</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        entry</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> ""</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    constructor</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">private</span><span style="color:#FFB86C;font-style:italic"> formBuilder</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> FormBuilder</span><span style="color:#F8F8F2">) {}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    async</span><span style="color:#50FA7B"> onSubmit</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">save</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    private</span><span style="color:#FF79C6"> async</span><span style="color:#50FA7B"> save</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> key </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> nanoid</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        await</span><span style="color:#50FA7B"> setDoc</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Entry</span><span style="color:#F8F8F2">>({</span></span>
<span class="line"><span style="color:#F8F8F2">            collection</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">notes</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">            doc</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">                key,</span></span>
<span class="line"><span style="color:#F8F8F2">                data</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">                    text</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.entryForm.value.entry</span></span>
<span class="line"><span style="color:#F8F8F2">                }</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p><em>Please note that for the sake of simplicity, the code snippets provided in this tutorial do not include proper error handling nor complex form handling.</em></p>
<p>In the above code snippet, we are persisting an object called “entries” to the blockchain. For the purposes of this tutorial, we have declared the type of the object in our frontend code as “Entry”. The documents are persisted on the blockchain as blobs, which means that you can persist any structure that can be serialized.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> interface</span><span style="color:#8BE9FD;font-style:italic"> Entry</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    text</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    url</span><span style="color:#FF79C6">?:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>Listing Documents</h3>
<p>To retrieve the list of documents saved on the blockchain, we can use the <code>listDocs</code> function provided by the library. This function can accept various parameters to filter, order, or paginate the data.</p>
<p>In this tutorial, we simply list all data of the users while observing the authentication state with the service we declared previously. If a user is set, we fetch the data; if none, we reset the entries. This is possible because every time the user signs in or out, the state will automatically be reflected.</p>
<p>In addition, we create also a dedicated service to keep the data in memory for reusability purposes. This service includes a reload function, which can be useful to reload the data as needed.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Inject, Injectable } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#FF79C6"> type</span><span style="color:#F8F8F2"> { Doc } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@junobuild/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { listDocs } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@junobuild/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#FF79C6"> type</span><span style="color:#F8F8F2"> { Observable } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">rxjs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Subject, combineLatest, from, map, of, shareReplay, startWith, switchMap } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">rxjs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#FF79C6"> type</span><span style="color:#F8F8F2"> { Entry } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">../types/entry</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { AuthService } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./auth.service</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Injectable</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    providedIn</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">root</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> DocsService</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    private</span><span style="color:#F8F8F2"> reloadSubject </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Subject</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">void</span><span style="color:#F8F8F2">>();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    docs$</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Observable</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Doc</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Entry</span><span style="color:#F8F8F2">>[]> </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> combineLatest</span><span style="color:#F8F8F2">([</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        this</span><span style="color:#F8F8F2">.authService.user$,</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        this</span><span style="color:#F8F8F2">.reloadSubject.</span><span style="color:#50FA7B">pipe</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">startWith</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">undefined</span><span style="color:#F8F8F2">))</span></span>
<span class="line"><span style="color:#F8F8F2">    ]).</span><span style="color:#50FA7B">pipe</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#50FA7B">        switchMap</span><span style="color:#F8F8F2">(([</span><span style="color:#FFB86C;font-style:italic">user</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">_</span><span style="color:#F8F8F2">]) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">            if</span><span style="color:#F8F8F2"> (user </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> null</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">                return</span><span style="color:#50FA7B"> of</span><span style="color:#F8F8F2">([]);</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#50FA7B"> from</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#50FA7B">                listDocs</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Entry</span><span style="color:#F8F8F2">>({</span></span>
<span class="line"><span style="color:#F8F8F2">                    collection</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">notes</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">                    filter</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {}</span></span>
<span class="line"><span style="color:#F8F8F2">                })</span></span>
<span class="line"><span style="color:#F8F8F2">            ).</span><span style="color:#50FA7B">pipe</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">map</span><span style="color:#F8F8F2">(({ </span><span style="color:#FFB86C;font-style:italic">items</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> items));</span></span>
<span class="line"><span style="color:#F8F8F2">        }),</span></span>
<span class="line"><span style="color:#50FA7B">        startWith</span><span style="color:#F8F8F2">([]),</span></span>
<span class="line"><span style="color:#50FA7B">        shareReplay</span><span style="color:#F8F8F2">({ bufferSize</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 1</span><span style="color:#F8F8F2">, refCount</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2"> })</span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    constructor</span><span style="color:#F8F8F2">(@</span><span style="color:#50FA7B">Inject</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B;font-style:italic">AuthService</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">private</span><span style="color:#FF79C6"> readonly</span><span style="color:#FFB86C;font-style:italic"> authService</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> AuthService</span><span style="color:#F8F8F2">) {}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    reload</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        this</span><span style="color:#F8F8F2">.reloadSubject.</span><span style="color:#50FA7B">next</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>For display purposes, we can subscribe to the asynchronous stream as we would with any observable.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Component, Inject } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { BrowserModule } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@angular/platform-browser</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { </span><span style="color:#FF79C6">type</span><span style="color:#F8F8F2"> Doc } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@junobuild/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Observable } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">rxjs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { DocsService } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">../../services/docs.service</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#FF79C6"> type</span><span style="color:#F8F8F2"> { Entry } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">../../types/entry</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    selector</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">app-list</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    template</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> `&#x3C;p *ngFor="let doc of docs$ | async">{{ doc.key }}: {{ doc.data.text }}&#x3C;/p>`</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    imports</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#50FA7B;font-style:italic">BrowserModule</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">    standalone</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> ListComponent</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    readonly</span><span style="color:#F8F8F2"> docs$</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Observable</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Doc</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Entry</span><span style="color:#F8F8F2">>[]> </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.docsService.docs$;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    constructor</span><span style="color:#F8F8F2">(@</span><span style="color:#50FA7B">Inject</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B;font-style:italic">DocsService</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">private</span><span style="color:#FF79C6"> readonly</span><span style="color:#FFB86C;font-style:italic"> docsService</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> DocsService</span><span style="color:#F8F8F2">) {}</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>Uploading File</h3>
<p>Storing user-generated content on Web3 can be a challenge, but Juno makes it easy for app developers. It’s designed to enable the storage and serving of assets such as photos or videos effortlessly.</p>
<p>To upload files, you’ll need to create a collection by following the instructions in the <a href="https://juno.build/docs/build/storage#collections-and-rules" rel="noopener noreferrer external">documentation</a>. In this tutorial, we’ll focus on image uploads, so the collection should be named “images.”</p>
<p>Each file stored on the blockchain is identified by a unique filename and path that corresponds to a unique URL. To accomplish this, we create a key using a combination of the user’s unique ID in text form and a timestamp for each uploaded file. We can retrieve the corresponding user’s key by accessing the observable of the authentication service we declared in previous chapter.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Component, Inject } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { BrowserModule } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@angular/platform-browser</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { AssetKey, User, uploadFile } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@junobuild/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { filter, from, switchMap, take } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">rxjs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { AuthService } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">../../services/auth.service</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    selector</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">app-upload</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    template</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> `</span></span>
<span class="line"><span style="color:#F1FA8C">        &#x3C;input type="file" accept="image/png, image/gif, image/jpeg" (change)="onFileChanged($event)" /></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F1FA8C">        &#x3C;img *ngIf="downloadUrl !== undefined" [src]="downloadUrl" loading="lazy" /></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F1FA8C">        &#x3C;button (click)="add()">Upload&#x3C;/button></span></span>
<span class="line"><span style="color:#F1FA8C">    `</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    standalone</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    imports</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#50FA7B;font-style:italic">BrowserModule</span><span style="color:#F8F8F2">]</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> UploadComponent</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    private</span><span style="color:#F8F8F2"> file</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> File</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    downloadUrl</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    constructor</span><span style="color:#F8F8F2">(@</span><span style="color:#50FA7B">Inject</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B;font-style:italic">AuthService</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">private</span><span style="color:#FF79C6"> readonly</span><span style="color:#FFB86C;font-style:italic"> authService</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> AuthService</span><span style="color:#F8F8F2">) {}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    add</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        this</span><span style="color:#F8F8F2">.authService.user$</span></span>
<span class="line"><span style="color:#F8F8F2">            .</span><span style="color:#50FA7B">pipe</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#50FA7B">                filter</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">user</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> user </span><span style="color:#FF79C6">!==</span><span style="color:#BD93F9"> null</span><span style="color:#F8F8F2">),</span></span>
<span class="line"><span style="color:#50FA7B">                switchMap</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">user</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> from</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">upload</span><span style="color:#F8F8F2">(user </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> User</span><span style="color:#F8F8F2">))),</span></span>
<span class="line"><span style="color:#50FA7B">                take</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">            )</span></span>
<span class="line"><span style="color:#F8F8F2">            .</span><span style="color:#50FA7B">subscribe</span><span style="color:#F8F8F2">(({ </span><span style="color:#FFB86C;font-style:italic">downloadUrl</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">                this</span><span style="color:#F8F8F2">.downloadUrl </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> downloadUrl;</span></span>
<span class="line"><span style="color:#F8F8F2">            });</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    onFileChanged</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">$event</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Event</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> target </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> $event.target </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> HTMLInputElement</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        this</span><span style="color:#F8F8F2">.file </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> target.files?.[</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">];</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    private</span><span style="color:#FF79C6"> async</span><span style="color:#50FA7B"> upload</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">user</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> User</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">AssetKey</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> filename </span><span style="color:#FF79C6">=</span><span style="color:#F1FA8C"> `</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">user.key</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">-</span><span style="color:#FF79C6">${</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.file.name</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#50FA7B"> uploadFile</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">            collection</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">images</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">            data</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.file,</span></span>
<span class="line"><span style="color:#F8F8F2">            filename</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Once an asset is uploaded, a <code>downloadUrl</code> is returned which provides a direct HTTPS link to access the uploaded asset on the web.</p>
<hr />
<h3>Listing Assets</h3>
<p>To fetch the list of assets saved on the blockchain, we can use the <code>listAssets</code> function provided by the library. This function can accept various parameters to filter, order, or paginate the files.</p>
<p>Similar to the documents, we can create a service that converts the list into an observable.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Inject, Injectable } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#FF79C6"> type</span><span style="color:#F8F8F2"> { Assets } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@junobuild/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { listAssets } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@junobuild/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#FF79C6"> type</span><span style="color:#F8F8F2"> { Observable } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">rxjs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Subject, combineLatest, from, map, of, shareReplay, startWith, switchMap } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">rxjs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { AuthService } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./auth.service</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Injectable</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    providedIn</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">root</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> AssetsService</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    private</span><span style="color:#F8F8F2"> reloadSubject </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Subject</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">void</span><span style="color:#F8F8F2">>();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    assets$</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Observable</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Assets</span><span style="color:#F8F8F2">[]> </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> combineLatest</span><span style="color:#F8F8F2">([</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        this</span><span style="color:#F8F8F2">.authService.user$,</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        this</span><span style="color:#F8F8F2">.reloadSubject.</span><span style="color:#50FA7B">pipe</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">startWith</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">undefined</span><span style="color:#F8F8F2">))</span></span>
<span class="line"><span style="color:#F8F8F2">    ]).</span><span style="color:#50FA7B">pipe</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#50FA7B">        switchMap</span><span style="color:#F8F8F2">(([</span><span style="color:#FFB86C;font-style:italic">user</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">_</span><span style="color:#F8F8F2">]) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">            if</span><span style="color:#F8F8F2"> (user </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> null</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">                return</span><span style="color:#50FA7B"> of</span><span style="color:#F8F8F2">([]);</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#50FA7B"> from</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#50FA7B">                listAssets</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">                    collection</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">images</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">                    filter</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {}</span></span>
<span class="line"><span style="color:#F8F8F2">                })</span></span>
<span class="line"><span style="color:#F8F8F2">            ).</span><span style="color:#50FA7B">pipe</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">map</span><span style="color:#F8F8F2">(({ </span><span style="color:#FFB86C;font-style:italic">assets</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> assets));</span></span>
<span class="line"><span style="color:#F8F8F2">        }),</span></span>
<span class="line"><span style="color:#50FA7B">        startWith</span><span style="color:#F8F8F2">([]),</span></span>
<span class="line"><span style="color:#50FA7B">        shareReplay</span><span style="color:#F8F8F2">({ bufferSize</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 1</span><span style="color:#F8F8F2">, refCount</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2"> })</span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    constructor</span><span style="color:#F8F8F2">(@</span><span style="color:#50FA7B">Inject</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B;font-style:italic">AuthService</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">private</span><span style="color:#FF79C6"> readonly</span><span style="color:#FFB86C;font-style:italic"> authService</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> AuthService</span><span style="color:#F8F8F2">) {}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    reload</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        this</span><span style="color:#F8F8F2">.reloadSubject.</span><span style="color:#50FA7B">next</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>For display purposes, we also subscribe to the asynchronous stream as we would with any observable.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Component, Inject } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { BrowserModule } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@angular/platform-browser</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Asset } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@junobuild/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Observable } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">rxjs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { AssetsService } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">../../services/assets.service</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    selector</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">app-assets</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    template</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> `&#x3C;img</span></span>
<span class="line"><span style="color:#F1FA8C">        *ngFor="let asset of assets$ | async"</span></span>
<span class="line"><span style="color:#F1FA8C">        [src]="asset.downloadUrl"</span></span>
<span class="line"><span style="color:#F1FA8C">        loading="lazy"</span></span>
<span class="line"><span style="color:#F1FA8C">    />`</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    imports</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#50FA7B;font-style:italic">BrowserModule</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">    standalone</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> AssetsComponent</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    readonly</span><span style="color:#F8F8F2"> assets$</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Observable</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Asset</span><span style="color:#F8F8F2">[]> </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.assetsService.assets$;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    constructor</span><span style="color:#F8F8F2">(@</span><span style="color:#50FA7B">Inject</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B;font-style:italic">AssetsService</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">private</span><span style="color:#FF79C6"> readonly</span><span style="color:#FFB86C;font-style:italic"> assetsService</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> AssetsService</span><span style="color:#F8F8F2">) {}</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>Deployment 🚀</h3>
<p>Once your application is developed and built, the next step is to launch it on the blockchain. To accomplish this, you must install the Juno <a href="https://juno.build/docs/miscellaneous/cli" rel="noopener noreferrer external">command line interface</a> by executing the following command in your terminal:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> i</span><span style="color:#BD93F9"> -g</span><span style="color:#F1FA8C"> @junobuild/cli</span></span></code></pre><p>After the installation process is finished, you can gain access to your satellite by following the instructions in the <a href="https://juno.build/docs/miscellaneous/cli#login" rel="noopener noreferrer external">documentation</a> and logging in from the terminal. This will enable your machine to control your satellite.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">juno</span><span style="color:#F1FA8C"> login</span></span></code></pre><p>Finally, you can deploy your project using the following command:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">juno</span><span style="color:#F1FA8C"> deploy</span></span></code></pre><p>Congratulations! Your Angular app is now decentralized 🎉.</p>
<hr />
<h2>Resources</h2>
<ul>
<li>Juno documentation and getting started: <a href="https://juno.build/docs/intro" rel="noopener noreferrer external">https://juno.build/docs/intro</a></li>
<li>The source code of this tutorial is available in our <a href="https://github.com/junobuild/examples/tree/main/angular/diary" rel="noopener noreferrer external">GitHub Repository</a>.</li>
</ul>
<p>To infinity and beyond<br />
David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Build A Web3 App With React JS]]></title>
          <description><![CDATA[How to develop a decentralized application on blockchain with React and Juno.]]></description>
          <link>https://daviddalbusco.com/blog/build-a-web3-app-with-react-js/</link>
          <pubDate>Tue, 28 Mar 2023 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://images.unsplash.com/photo-1506878206813-92402b8ded23?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwzMDkyMzV8MHwxfHNlYXJjaHwxfHxDb21wdXRlciUyMEdyb25pbmdlbiUyQyUyME5ldGhlcmxhbmRzfGVufDB8fHx8MTY3OTk0MzA1OQ&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080" alt="Working on Sunday" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@jantined?utm_source=Papyrs&amp;utm_medium=referral" rel="noopener noreferrer external">Jantine Doornbos</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<hr />
<h2>Introduction</h2>
<p>There are numerous solutions available for building on Web3, each with their own unique advantages and limitations, but most are often related to connecting wallets and executing transactions. However, if you are a frontend JavaScript developer looking to build on the decentralized web, you may have found that the development experience can be quite different from the familiar world of Web2.</p>
<p>That’s why <a href="https://juno.build" rel="noopener noreferrer external">Juno</a> takes a different approach, aiming to harness the power of Web3 without sacrificing the ease and familiarity of Web2 development.</p>
<p>In this blog post, we’ll explore how to combine React and Juno to develop a dApp. So, let’s dive in and discover how Juno can help you build powerful and user-friendly decentralized applications!</p>
<hr />
<h2>How Juno Works</h2>
<p>Juno is an open-source Blockchain-as-a-Service platform. It works just like traditional serverless platforms such as Google Firebase or AWS Amplify, but with one key difference: everything on Juno runs on the blockchain. This means that you get a fully decentralized and secure infrastructure for your applications, which is pretty cool if you ask me.</p>
<p>Behind the scenes, Juno uses the <a href="https://internetcomputer.org/" rel="noopener noreferrer external">Internet Computer</a> blockchain network and infrastructure to launch what we call a “Satellite” for each app you build. A Satellite is essentially a smart contract on steroids that contains your entire app. From its assets provided on the web (such as JavaScript, HTML, and image files) to its state saved in a super simple database, file storage, and authentication, each Satellite controlled solely by you contains everything it needs to run smoothly.</p>
<hr />
<h2>Build Your First DApp</h2>
<p>Let’s build our first dapp! In this example, we will create a simple note-taking app that allows users to store data entries, upload files, and retrieve them as needed.</p>
<hr />
<h3>Initialization</h3>
<p>Before you can integrate Juno into your ReactJS app, you’ll need to create a satellite. This process is explained in detail in the <a href="https://juno.build/docs/add-juno-to-an-app/create-a-satellite" rel="noopener noreferrer external">documentation</a>.</p>
<p>Moreover, you also need to install the SDK.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> i</span><span style="color:#F1FA8C"> @junobuild/core</span></span></code></pre><p>After completing both of these steps, you can initialize Juno with your satellite ID at the top of your React app. This will configure the library to communicate with your smart contract.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { useEffect } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { initJuno } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@junobuild/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">function</span><span style="color:#50FA7B"> App</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#50FA7B">    useEffect</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        (</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span></span>
<span class="line"><span style="color:#FF79C6">            await</span><span style="color:#50FA7B"> initJuno</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">                satelliteId</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">pycrs-xiaaa-aaaal-ab6la-cai</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">            }))();</span></span>
<span class="line"><span style="color:#F8F8F2">    }, []);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">>Hello World&#x3C;/</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> App;</span></span></code></pre><p>That’s it for the configuration! Your app is now ready for Web3! 😎</p>
<hr />
<h3>Authentication</h3>
<p>To securely identify users <strong>anonymously</strong>, they will need to sign in and sign out. You can bind the related functions to call-to-actions anywhere in your app.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { signIn, signOut } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@junobuild/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">button</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">  type</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">button</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">  onClick</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">signIn</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#F8F8F2">>Login&#x3C;/</span><span style="color:#FF79C6">button</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">button</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">  type</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">button</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">  onClick</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">signOut</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#F8F8F2">>Logout&#x3C;/</span><span style="color:#FF79C6">button</span><span style="color:#F8F8F2">></span></span></code></pre><p>To integrate tightly with other services, the library and satellite automatically create a new entry in your smart contract when a user successfully signs in. This enables the library to check permissions on any exchange of data.</p>
<p>To observe this entry and, by extension, get to know the user’s state, Juno provides an observable function called <code>authSubscribe()</code>. You can use it as many times as required, but I find it convenient to subscribe to it at the top of an app. This way, we can create a <code>Context</code> to propagate the user.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { createContext, useEffect, useState } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { authSubscribe } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@junobuild/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> AuthContext </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> createContext</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> Auth</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">children</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> [user, setUser] </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useState</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">undefined</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    useEffect</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> sub </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> authSubscribe</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">user</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> setUser</span><span style="color:#F8F8F2">(user));</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> unsubscribe</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    }, []);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">AuthContext.Provider</span><span style="color:#50FA7B;font-style:italic"> value</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">{ user }</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#FF79C6">            {</span><span style="color:#F8F8F2">user </span><span style="color:#FF79C6">!==</span><span style="color:#BD93F9"> undefined</span><span style="color:#FF79C6"> &#x26;&#x26;</span><span style="color:#F8F8F2"> user </span><span style="color:#FF79C6">!==</span><span style="color:#BD93F9"> null</span><span style="color:#FF79C6"> ?</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span><span style="color:#FF79C6">{</span><span style="color:#F8F8F2">children</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">>Not signed in.&#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">AuthContext.Provider</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Juno’s library is framework-agnostic and currently does not include any framework-specific code. However, we welcome contributions from the community. If you are interested in providing React plugins, contexts, hooks or else, feel free to contribute to the project! 💪</p>
<hr />
<h3>Storing Documents</h3>
<p>Storing data on the blockchain with Juno is done through a feature called “Datastore”. A datastore consists of a list of collections that contain your documents, each identified by a textual key that you define.</p>
<p>In this tutorial, our goal is to store notes. To achieve this, you will need to follow the instructions in the <a href="https://juno.build/docs/build/datastore#collections-and-rules" rel="noopener noreferrer external">documentation</a> to create a collection, which can be named accordingly (“notes”).</p>
<p>Once your app is set up and your collection is created, we can persist data on the blockchain using the <code>setDoc</code> function provided by the library.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { setDoc } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@junobuild/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// TypeScript example from the documentation</span></span>
<span class="line"><span style="color:#FF79C6">await</span><span style="color:#50FA7B"> setDoc</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Example</span><span style="color:#F8F8F2">>({</span></span>
<span class="line"><span style="color:#F8F8F2">    collection</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">my_collection_key</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    doc</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        key</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">my_document_key</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        data</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> myExample</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><p>Since the documents in the collection are identified by a unique key, we create keys using <a href="https://github.com/ai/nanoid" rel="noopener noreferrer external">nanoid</a> — a tiny string ID generator for JavaScript.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { useState } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { setDoc } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@junobuild/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { nanoid } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">nanoid</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> Example</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> [inputText, setInputText] </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useState</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">""</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#50FA7B"> add</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        await</span><span style="color:#50FA7B"> setDoc</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">            collection</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">data</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">            doc</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">                key</span><span style="color:#FF79C6">:</span><span style="color:#50FA7B"> nanoid</span><span style="color:#F8F8F2">(),</span></span>
<span class="line"><span style="color:#F8F8F2">                data</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">                    text</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> inputText</span></span>
<span class="line"><span style="color:#F8F8F2">                }</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">textarea</span><span style="color:#50FA7B;font-style:italic"> onChange</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">e</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> setInputText</span><span style="color:#F8F8F2">(e.target.value)</span><span style="color:#FF79C6">}</span><span style="color:#50FA7B;font-style:italic"> value</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">inputText</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">textarea</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">button</span><span style="color:#50FA7B;font-style:italic"> type</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">button</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> onClick</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">add</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                Add</span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#FF79C6">button</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/></span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><hr />
<h3>Listing Documents</h3>
<p>To fetch the list of documents saved on the blockchain, we can use the <code>listDocs</code> function provided by the library. This function can accept various parameters to filter, order, or paginate the data.</p>
<p>In this tutorial, we’ll keep the example minimal. We simply list all data of the users while observing the <code>Context</code> we declared previously. If a user is set, we fetch the data; if none, we reset the entries. This is possible because every time the user signs in or out, the state will automatically be reflected.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { useContext, useEffect, useState } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { AuthContext } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./Auth</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { listDocs } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@junobuild/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> ListExample</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { user } </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useContext</span><span style="color:#F8F8F2">(AuthContext);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> [items, setItems] </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useState</span><span style="color:#F8F8F2">([]);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#50FA7B"> list</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> { items } </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> listDocs</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">            collection</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">notes</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">            filter</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {}</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">        setItems</span><span style="color:#F8F8F2">(items);</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    useEffect</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> ([</span><span style="color:#BD93F9">undefined</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">null</span><span style="color:#F8F8F2">].</span><span style="color:#50FA7B">includes</span><span style="color:#F8F8F2">(user)) {</span></span>
<span class="line"><span style="color:#50FA7B">            setItems</span><span style="color:#F8F8F2">([]);</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        (</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> list</span><span style="color:#F8F8F2">())();</span></span>
<span class="line"><span style="color:#F8F8F2">    }, [user]);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;></span></span>
<span class="line"><span style="color:#FF79C6">            {</span><span style="color:#F8F8F2">items.</span><span style="color:#50FA7B">map</span><span style="color:#F8F8F2">(({ </span><span style="color:#FFB86C;font-style:italic">key</span><span style="color:#F8F8F2">, data: { </span><span style="color:#FFB86C;font-style:italic">text</span><span style="color:#F8F8F2"> } }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#50FA7B;font-style:italic"> key</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">key</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span><span style="color:#FF79C6">{</span><span style="color:#F8F8F2">text</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            ))</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/></span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><hr />
<h3>Uploading File</h3>
<p>Storing data on the decentralized web isn’t always that easy. Fortunately, Juno is not one of those and is designed for app developers who need to store and serve user-generated content, such as photos or videos, with ease.</p>
<p>As for the documents, to upload assets you will need first to follow the instructions in the <a href="https://juno.build/docs/build/storage#collections-and-rules" rel="noopener noreferrer external">documentation</a> to create a collection. In this tutorial we will implement image uploads, so the collection can be named accordingly (“images”).</p>
<p>The stored data are identified with unique file names and paths. This is because the data are provided on the web and therefore each piece of data should match a unique URL.</p>
<p>To achieve this, we can create a key using a combination of the unique user’s ID in its textual representation and a timestamp for each file uploaded. We can get access the property we passed down through the <code>Context</code> in the previous chapter to retrieve the corresponding user’s key.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { useContext, useState } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { AuthContext } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./Auth</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { uploadFile } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@junobuild/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> UploadExample</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> [file, setFile] </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useState</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> [image, setImage] </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useState</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { user } </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useContext</span><span style="color:#F8F8F2">(AuthContext);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#50FA7B"> add</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> filename </span><span style="color:#FF79C6">=</span><span style="color:#F1FA8C"> `</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">user.key</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">-</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">file.name</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> { downloadUrl } </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> uploadFile</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">            collection</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">images</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">            data</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> file,</span></span>
<span class="line"><span style="color:#F8F8F2">            filename</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">        setImage</span><span style="color:#F8F8F2">(downloadUrl);</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">input</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                type</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">file</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                accept</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">image/png, image/gif, image/jpeg</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                onChange</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">event</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> setFile</span><span style="color:#F8F8F2">(event.target.files?.[</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">])</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#F8F8F2">            /></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">button</span><span style="color:#50FA7B;font-style:italic"> type</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">button</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> onClick</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">add</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                Add</span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#FF79C6">button</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">            {</span><span style="color:#F8F8F2">image </span><span style="color:#FF79C6">!==</span><span style="color:#BD93F9"> undefined</span><span style="color:#FF79C6"> &#x26;&#x26;</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FF79C6">img</span><span style="color:#50FA7B;font-style:italic"> src</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">image</span><span style="color:#FF79C6">}</span><span style="color:#50FA7B;font-style:italic"> loading</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">lazy</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/></span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Once an asset is uploaded, a <code>downloadUrl</code> is returned which provides a direct HTTPS link to access the uploaded asset on the web.</p>
<hr />
<h3>Listing Assets</h3>
<p>To fetch the list of assets saved on the blockchain, we can use the <code>listAssets</code> function provided by the library. This function can accept various parameters to filter, order, or paginate the files.</p>
<p>As for the documents previously, we’ll keep the example minimal. We simply list all assets of the users observing the <code>Context</code>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { useContext, useEffect, useState } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { AuthContext } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./Auth</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { listAssets } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@junobuild/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> ListAssetsExample</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { user } </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useContext</span><span style="color:#F8F8F2">(AuthContext);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> [assets, setAssets] </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useState</span><span style="color:#F8F8F2">([]);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#50FA7B"> list</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> { assets } </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> listAssets</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">            collection</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">images</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">            filter</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {}</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">        setAssets</span><span style="color:#F8F8F2">(assets);</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    useEffect</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> ([</span><span style="color:#BD93F9">undefined</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">null</span><span style="color:#F8F8F2">].</span><span style="color:#50FA7B">includes</span><span style="color:#F8F8F2">(user)) {</span></span>
<span class="line"><span style="color:#50FA7B">            setAssets</span><span style="color:#F8F8F2">([]);</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        (</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> list</span><span style="color:#F8F8F2">())();</span></span>
<span class="line"><span style="color:#F8F8F2">    }, [user]);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;></span></span>
<span class="line"><span style="color:#FF79C6">            {</span><span style="color:#F8F8F2">assets.</span><span style="color:#50FA7B">map</span><span style="color:#F8F8F2">(({ </span><span style="color:#FFB86C;font-style:italic">fullPath</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">downloadUrl</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF79C6">img</span><span style="color:#50FA7B;font-style:italic"> key</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">fullPath</span><span style="color:#FF79C6">}</span><span style="color:#50FA7B;font-style:italic"> loading</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">lazy</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> src</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">downloadUrl</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">            ))</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/></span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><hr />
<h3>Deployment 🚀</h3>
<p>After you have developed and built your application, you can launch it on the blockchain. To do this, you will need to install the Juno <a href="https://juno.build/docs/miscellaneous/cli" rel="noopener noreferrer external">command line interface</a> by running the following command in your terminal:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> i</span><span style="color:#BD93F9"> -g</span><span style="color:#F1FA8C"> @junobuild/cli</span></span></code></pre><p>Once the installation is complete, you can log in to your satellite from the terminal using the instructions in the <a href="https://juno.build/docs/miscellaneous/cli#login" rel="noopener noreferrer external">documentation</a>. This will grant control of your machine to your satellite:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">juno</span><span style="color:#F1FA8C"> login</span></span></code></pre><p>Finally, you can deploy your project using the following command:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">juno</span><span style="color:#F1FA8C"> deploy</span></span></code></pre><p>Congratulations! Your app is now decentralized 🎉.</p>
<hr />
<h2>Ressources</h2>
<ul>
<li>Juno documentation and getting started: <a href="https://juno.build/docs/intro" rel="noopener noreferrer external">https://juno.build/docs/intro</a></li>
<li>Code source of this tutorial: <a href="https://github.com/junobuild/examples/tree/main/react/diary" rel="noopener noreferrer external">https://github.com/junobuild/examples/tree/main/react/diary</a></li>
<li>The outcome of this article: <a href="https://pycrs-xiaaa-aaaal-ab6la-cai.icp0.io/" rel="noopener noreferrer external">https://pycrs-xiaaa-aaaal-ab6la-cai.icp0.io/</a></li>
</ul>
<p>To infinity and beyond<br />
David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Automatically generate Candid from Rust on the IC]]></title>
          <description><![CDATA[How to auto-generate the Candid declaration from Rust code on the Internet Computer.]]></description>
          <link>https://daviddalbusco.com/blog/automatically-generate-candid-from-rust-on-the-ic/</link>
          <pubDate>Sun, 12 Mar 2023 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://images.unsplash.com/photo-1466611653911-95081537e5b7?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwzMDkyMzV8MHwxfHNlYXJjaHwxOXx8d2luZG1pbGwlMjBvbiUyMGdyYXNzJTIwZmllbGR8ZW58MHx8fHwxNjc4NjUwMzQ1&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080" alt="Follow my Instagram @karsten.wuerth" / loading="lazy"></p>
<p><em><a href="https://unsplash.com/fr/@karsten_wuerth?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Karsten Würth</a> on <a href="https://unsplash.com/fr/photos/0w-uTa0Xz7w?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<hr />
<blockquote>
<p>Update July 22nd, 2023: There is now a better way to automatically generate Candid from Rust. Check out my new <a href="https://daviddalbusco.com/blog/automatic-candid-generation-in-rust-exploring-the-ic-cdk-v0-10-0-update/" rel="noopener noreferrer external">blog post</a> about that subject!</p>
</blockquote>
<hr />
<p>The ability to auto-generate the Candid declaration from Rust code on the Internet Computer is expected to become available in the second quarter of 2023.</p>
<p>In the meantime, a workaround can be used to generate these types, which I notably use in my open-source Blockchain-as-a-Service project, <a href="https://juno.build/" rel="noopener noreferrer external">Juno</a>.</p>
<p>Here’s how you can implement the workaround yourself.</p>
<hr />
<h2>1. Annotate</h2>
<p>Because this solution involves a workaround, the first step is to annotate the public methods that need to be exported to Candid types.</p>
<p>To do this, use the <code>candid\_method</code> macro of the Candid <a href="https://docs.rs/candid/latest/candid/index.html" rel="noopener noreferrer external">crate</a>, specifying the export type and, if necessary, the <code>query</code> type. The default is <code>update</code>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">use</span><span style="color:#F8F8F2"> ic_cdk_macros</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">{query, update};</span></span>
<span class="line"><span style="color:#FF79C6">use</span><span style="color:#F8F8F2"> ic_cdk</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">export</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">candid</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">{candid_method};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">#[candid_method(query)]</span></span>
<span class="line"><span style="color:#F8F8F2">#[query]</span></span>
<span class="line"><span style="color:#FF79C6">fn</span><span style="color:#50FA7B"> hello</span><span style="color:#F8F8F2">(name</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> String</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> String</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    format!</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">"Hello, {}!"</span><span style="color:#F8F8F2">, name)</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">#[candid_method]</span></span>
<span class="line"><span style="color:#F8F8F2">#[update]</span></span>
<span class="line"><span style="color:#FF79C6">fn</span><span style="color:#50FA7B"> world</span><span style="color:#F8F8F2">(name</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> String</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> String</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    format!</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">"World, {}!"</span><span style="color:#F8F8F2">, name)</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h2>2. Collect and generate</h2>
<p>To collect the methods that we need to generate their declarations, we use the <code>export\_service</code> macro from the Candid crate. We add a <code>query</code> method and prefix its name with two underscores, as it has no practical purpose for our canister.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">use</span><span style="color:#F8F8F2"> ic_cdk</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">export</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">candid</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">{export_service};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">#[query(name </span><span style="color:#FF79C6">=</span><span style="color:#F1FA8C"> "__get_candid_interface_tmp_hack"</span><span style="color:#F8F8F2">)]</span></span>
<span class="line"><span style="color:#FF79C6">fn</span><span style="color:#50FA7B"> export_candid</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> String</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    export_service!</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#50FA7B">    __export_service</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h2>3. Save to file system with a test</h2>
<p>Since we need a hook to initiate the generation of the DID files, we use the test runner to execute the necessary steps. This is why we use a test module for this purpose.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">#[cfg(test)]</span></span>
<span class="line"><span style="color:#FF79C6">mod</span><span style="color:#F8F8F2"> tests {</span></span>
<span class="line"><span style="color:#FF79C6">    use</span><span style="color:#BD93F9;font-style:italic"> super</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">export_candid;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    #[test]</span></span>
<span class="line"><span style="color:#FF79C6">    fn</span><span style="color:#50FA7B"> save_candid</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        use</span><span style="color:#F8F8F2"> std</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">env;</span></span>
<span class="line"><span style="color:#FF79C6">        use</span><span style="color:#F8F8F2"> std</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">fs</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">write;</span></span>
<span class="line"><span style="color:#FF79C6">        use</span><span style="color:#F8F8F2"> std</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">path</span><span style="color:#FF79C6">::</span><span style="color:#8BE9FD;font-style:italic">PathBuf</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        let</span><span style="color:#F8F8F2"> dir </span><span style="color:#FF79C6">=</span><span style="color:#8BE9FD;font-style:italic"> PathBuf</span><span style="color:#FF79C6">::</span><span style="color:#50FA7B">from</span><span style="color:#F8F8F2">(env</span><span style="color:#FF79C6">::</span><span style="color:#50FA7B">var</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">"CARGO_MANIFEST_DIR"</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">unwrap</span><span style="color:#F8F8F2">());</span></span>
<span class="line"><span style="color:#FF79C6">        let</span><span style="color:#F8F8F2"> dir </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> dir</span></span>
<span class="line"><span style="color:#FF79C6">            .</span><span style="color:#50FA7B">parent</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#FF79C6">            .</span><span style="color:#50FA7B">unwrap</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#FF79C6">            .</span><span style="color:#50FA7B">parent</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#FF79C6">            .</span><span style="color:#50FA7B">unwrap</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#FF79C6">            .</span><span style="color:#50FA7B">join</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">"src"</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#FF79C6">            .</span><span style="color:#50FA7B">join</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">"demo"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#50FA7B">        write</span><span style="color:#F8F8F2">(dir</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">join</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">"demo.did"</span><span style="color:#F8F8F2">), </span><span style="color:#50FA7B">export_candid</span><span style="color:#F8F8F2">())</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">expect</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">"Write failed."</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>The above snippet is designed for a canister named <code>demo</code> and a file structure relative to a standard sample architecture <code>root/src/demo/Cargo.toml</code>. Please update the path and name based on your project requirements.</p>
<p>That's all! 😁 Simply run <code>cargo test</code> and a <code>did</code> file should be automatically generated.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">service </span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  hello </span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> (text) </span><span style="color:#FF79C6">-></span><span style="color:#F8F8F2"> (text) query;</span></span>
<span class="line"><span style="color:#F8F8F2">  world </span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> (text) </span><span style="color:#FF79C6">-></span><span style="color:#F8F8F2"> (text)</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h2>Conclusion</h2>
<p>Of course, you can always wait until Q2 or avoid these shenanigans by using <a href="https://juno.build" rel="noopener noreferrer external">Juno</a> (😄). Nonetheless, I hope this short blog post will be useful to you.</p>
<p>To infinity and beyond!<br />
David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Rust Trait: A Powerful Alternative To TypeScript Interface]]></title>
          <description><![CDATA[Comparing TypeScript “interface” with Rust “trait” for simple, flexible, and composable code.]]></description>
          <link>https://daviddalbusco.com/blog/rust-trait-a-powerful-alternative-to-typescript-interface/</link>
          <pubDate>Mon, 06 Mar 2023 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://images.unsplash.com/photo-1579547621706-1a9c79d5c9f1?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwzMDkyMzV8MHwxfHNlYXJjaHwxfHxHcmFkaWVudHxlbnwwfHx8fDE2NzgwMzYwNDI&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080" alt="Distorted Sun" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/pt-br/@gradienta?utm_source=Papyrs&amp;utm_medium=referral" rel="noopener noreferrer external">Gradienta</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>While Rust has a concept of interface, it differs from other programming languages in that it does not use the <code>interface</code> keyword to specify the behavior of classes and functions. Instead, Rust’s closest abstraction pattern is <code>trait</code>. Although these concepts have many differences, they both address the problem of having multiple possible implementations.</p>
<p>In this blog post, I will compare a TypeScript code snippet with its potential Rust equivalent to demonstrate how to achieve a simple flexible and composable code.</p>
<hr />
<h2>Declaration</h2>
<p>Let’s imagine a project that involves saving and listing documents and images in a database. Since both types of files are stored in the same storage and share common characteristics, we could use an <code>interface</code> to share this common information.</p>
<p>Using an interface would allow us to define a set of properties and methods that are common, making it easier to write code that works with either type of file.</p>
<p>In TypeScript, we can define this interface as follows:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">interface</span><span style="color:#8BE9FD;font-style:italic"> Entity</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    id</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    timestamp</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">interface</span><span style="color:#8BE9FD;font-style:italic"> Document</span><span style="color:#FF79C6"> extends</span><span style="color:#8BE9FD;font-style:italic"> Entity</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    revised</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">interface</span><span style="color:#8BE9FD;font-style:italic"> Image</span><span style="color:#FF79C6"> extends</span><span style="color:#8BE9FD;font-style:italic"> Entity</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    type</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Since Rust does not have inheritance, the simplest corresponding implementation would require us to duplicate the types.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">struct</span><span style="color:#8BE9FD;font-style:italic"> Document</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    id</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> String</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    timestamp</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> u64</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    revised</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> bool</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">struct</span><span style="color:#8BE9FD;font-style:italic"> Image</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    id</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> String</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    timestamp</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> u64</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    mime_type</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> String</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h2>Inheritance and Generic</h2>
<p>Let’s now consider a scenario where we want to find a specific document or image. In TypeScript, we can write the code to accomplish this as follows:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> getDocument</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">id</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">documents</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Document</span><span style="color:#F8F8F2">[])</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Document</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> =></span></span>
<span class="line"><span style="color:#F8F8F2">    documents.</span><span style="color:#50FA7B">find</span><span style="color:#F8F8F2">(({ id: </span><span style="color:#FFB86C;font-style:italic">docId</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> docId </span><span style="color:#FF79C6">===</span><span style="color:#F8F8F2"> id);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> getImages</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">id</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">images</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Image</span><span style="color:#F8F8F2">[])</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Image</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> =></span></span>
<span class="line"><span style="color:#F8F8F2">    images.</span><span style="color:#50FA7B">find</span><span style="color:#F8F8F2">(({ id: </span><span style="color:#FFB86C;font-style:italic">imageId</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> imageId </span><span style="color:#FF79C6">===</span><span style="color:#F8F8F2"> id);</span></span></code></pre><p>However, since both types implement the same interface, we can avoid duplication by extracting a generic function:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> get</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#FF79C6"> extends</span><span style="color:#FFB86C;font-style:italic"> Entity</span><span style="color:#F8F8F2">>(</span><span style="color:#FFB86C;font-style:italic">id</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">elements</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> T</span><span style="color:#F8F8F2">[])</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> T</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> =></span></span>
<span class="line"><span style="color:#F8F8F2">    elements.</span><span style="color:#50FA7B">find</span><span style="color:#F8F8F2">(({ id: </span><span style="color:#FFB86C;font-style:italic">elementId</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> elementId </span><span style="color:#FF79C6">===</span><span style="color:#F8F8F2"> id);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> getDocument</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">id</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">documents</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Document</span><span style="color:#F8F8F2">[])</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Document</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> =></span></span>
<span class="line"><span style="color:#50FA7B">    get</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Document</span><span style="color:#F8F8F2">>(id, documents);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> getImages</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">id</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">images</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Image</span><span style="color:#F8F8F2">[])</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Image</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> =></span><span style="color:#50FA7B"> get</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Image</span><span style="color:#F8F8F2">>(id, images);</span></span></code></pre><p>In Rust, implementing the same functionality would initially also require duplicating the code:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">fn</span><span style="color:#50FA7B"> get_document</span><span style="color:#F8F8F2">(id</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> String</span><span style="color:#F8F8F2">, documents</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Vec</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">Document</span><span style="color:#F8F8F2">>) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> Option</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">Document</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#F8F8F2">    documents</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">into_iter</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">find</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">|</span><span style="color:#F8F8F2">document</span><span style="color:#FF79C6">|</span><span style="color:#F8F8F2"> document</span><span style="color:#FF79C6">.</span><span style="color:#F8F8F2">id </span><span style="color:#FF79C6">==</span><span style="color:#F8F8F2"> id)</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">fn</span><span style="color:#50FA7B"> get_image</span><span style="color:#F8F8F2">(id</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> String</span><span style="color:#F8F8F2">, images</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Vec</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">Image</span><span style="color:#F8F8F2">>) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> Option</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">Image</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#F8F8F2">    images</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">into_iter</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">find</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">|</span><span style="color:#F8F8F2">image</span><span style="color:#FF79C6">|</span><span style="color:#F8F8F2"> image</span><span style="color:#FF79C6">.</span><span style="color:#F8F8F2">id </span><span style="color:#FF79C6">==</span><span style="color:#F8F8F2"> id)</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>As you can see, the Rust code closely resembles the TypeScript implementation. However, since Rust does not have inheritance or an <code>interface</code> keyword, we cannot exactly replicate the same pattern as above to avoid duplication. This is where <code>trait</code> come into play.</p>
<p>In this particular example, the common trait shared by both documents and images is that they can be compared using IDs. This is why we can declare this characteristic as a <code>trait</code> and provide a respective implementation for both ```<br />
struct.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">trait</span><span style="color:#8BE9FD;font-style:italic"> Compare</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    fn</span><span style="color:#50FA7B"> compare</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">&#x26;</span><span style="color:#BD93F9;font-style:italic">self</span><span style="color:#F8F8F2">, id</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#8BE9FD;font-style:italic">str</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> bool</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">impl</span><span style="color:#8BE9FD;font-style:italic"> Compare</span><span style="color:#FF79C6"> for</span><span style="color:#8BE9FD;font-style:italic"> Document</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    fn</span><span style="color:#50FA7B"> compare</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">&#x26;</span><span style="color:#BD93F9;font-style:italic">self</span><span style="color:#F8F8F2">, id</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#8BE9FD;font-style:italic">str</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> bool</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        self</span><span style="color:#FF79C6">.</span><span style="color:#F8F8F2">id </span><span style="color:#FF79C6">==</span><span style="color:#F8F8F2"> id</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">impl</span><span style="color:#8BE9FD;font-style:italic"> Compare</span><span style="color:#FF79C6"> for</span><span style="color:#8BE9FD;font-style:italic"> Image</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    fn</span><span style="color:#50FA7B"> compare</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">&#x26;</span><span style="color:#BD93F9;font-style:italic">self</span><span style="color:#F8F8F2">, id</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#8BE9FD;font-style:italic">str</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> bool</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        self</span><span style="color:#FF79C6">.</span><span style="color:#F8F8F2">id </span><span style="color:#FF79C6">==</span><span style="color:#F8F8F2"> id</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Finally, we can extract the common code to a generic function in Rust, just as we did earlier in TypeScript.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">fn</span><span style="color:#50FA7B"> get</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">T</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Compare</span><span style="color:#F8F8F2">>(id</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> String</span><span style="color:#F8F8F2">, elements</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Vec</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">T</span><span style="color:#F8F8F2">>) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> Option</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">T</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#F8F8F2">    elements</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">into_iter</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">find</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">|</span><span style="color:#F8F8F2">element</span><span style="color:#FF79C6">|</span><span style="color:#F8F8F2"> element</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">compare</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">&#x26;</span><span style="color:#F8F8F2">id))</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">fn</span><span style="color:#50FA7B"> get_document</span><span style="color:#F8F8F2">(id</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> String</span><span style="color:#F8F8F2">, documents</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Vec</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">Document</span><span style="color:#F8F8F2">>) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> Option</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">Document</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#50FA7B">    get</span><span style="color:#F8F8F2">(id, documents)</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">fn</span><span style="color:#50FA7B"> get_image</span><span style="color:#F8F8F2">(id</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> String</span><span style="color:#F8F8F2">, images</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Vec</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">Image</span><span style="color:#F8F8F2">>) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> Option</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">Image</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#50FA7B">    get</span><span style="color:#F8F8F2">(id, images)</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>It’s worth noting that <code>trait</code> in Rust can be combined using the “+” symbol, allowing us to define multiple common characteristics. For example:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">fn</span><span style="color:#50FA7B"> get</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">T</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Compare</span><span style="color:#FF79C6"> +</span><span style="color:#8BE9FD;font-style:italic"> OtherTrait</span><span style="color:#F8F8F2">>(id</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> String</span><span style="color:#F8F8F2">, elements</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Vec</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">T</span><span style="color:#F8F8F2">>) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> Option</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">T</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#F8F8F2">    elements</span></span>
<span class="line"><span style="color:#FF79C6">        .</span><span style="color:#50FA7B">into_iter</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#FF79C6">        .</span><span style="color:#50FA7B">find</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">|</span><span style="color:#F8F8F2">element</span><span style="color:#FF79C6">|</span><span style="color:#F8F8F2"> element</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">compare</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">&#x26;</span><span style="color:#F8F8F2">id) </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> element</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">other_trait</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">&#x26;</span><span style="color:#F8F8F2">id))</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Such pattern is also interesting to implement comparison of objects, as both parameter of the <code>trait</code> function can related to the same struct.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">trait</span><span style="color:#8BE9FD;font-style:italic"> Compare</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    fn</span><span style="color:#50FA7B"> sort</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">&#x26;</span><span style="color:#BD93F9;font-style:italic">self</span><span style="color:#F8F8F2">, other</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#BD93F9;font-style:italic">Self</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> Ordering</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">impl</span><span style="color:#8BE9FD;font-style:italic"> Compare</span><span style="color:#FF79C6"> for</span><span style="color:#8BE9FD;font-style:italic"> Document</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    fn</span><span style="color:#50FA7B"> sort</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">&#x26;</span><span style="color:#BD93F9;font-style:italic">self</span><span style="color:#F8F8F2">, other</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> &#x26;</span><span style="color:#BD93F9;font-style:italic">Self</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> Ordering</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        self</span><span style="color:#FF79C6">.</span><span style="color:#F8F8F2">timestamp</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">cmp</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">&#x26;</span><span style="color:#F8F8F2">other</span><span style="color:#FF79C6">.</span><span style="color:#F8F8F2">timestamp)</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h2>Conclusion</h2>
<p>While we have only scratched the surface of the power that <code>trait</code> can offer, I hope that this brief tutorial will prove useful to my fellow JavaScript developers who, like me, are exploring Rust.</p>
<p>To infinity and beyond<br />
David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Introducing Juno. An Open Source Blockchain Platform For Frontend Devs 🚀]]></title>
          <description><![CDATA[Launching a new open-source Blockchain-as-a-Service solution that makes building Web3 applications more accessible frontend developers.]]></description>
          <link>https://daviddalbusco.com/blog/introducing-juno-an-open-source-blockchain-platform-for-frontend-devs/</link>
          <pubDate>Tue, 14 Feb 2023 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>I am thrilled to introduce <a href="https://juno.build" rel="noopener noreferrer external">Juno</a>, a groundbreaking open-source Blockchain-as-a-Service solution that makes building Web3 applications more accessible to the millions of front-end developers worldwide.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*BufN6J5_X1q0Jjlep5DDdA.png" alt="Overview screenshot" / loading="lazy"></p>
<hr />
<h2>What is Juno?</h2>
<p>Unlike traditional Backend-as-a-Service (BaaS) platforms like Google Firebase or AWS Amplify, Juno runs entirely on the blockchain.</p>
<p>It allows you to forget about all the backend aspects of development, letting focus on what matters: the product your users see.</p>
<p>Our platform operates without controlling your data or work. With Juno, you truly own your creations.</p>
<p>The Juno closed beta program is launching today and is packed with all the essential features you need to start creating Web3 applications.</p>
<hr />
<h2>How It Works</h2>
<p>Every project you create on Juno is a &quot;Satellite&quot;: a smart contract that you own, running 100% on-chain. 🛰️</p>
<p>It is a container for your application that holds all your project's state: users' persisted data, asset storage (images or video) and your application bundle.</p>
<p>A satellite lives on its own -- and only <em>you</em> can administrate it, because you <em>own</em> it, exactly like an NFT.</p>
<hr />
<h2>Authentication</h2>
<p>Juno allows developers to securely identify users anonymously and save their data on the blockchain.</p>
<p>Our easy-to-use SDKs support authentication via <a href="https://internetcomputer.org/internet-identity" rel="noopener noreferrer external">Internet Identity</a>, a Web3 authentication provider that offers a secure blockchain login experience with a user-friendly Web2 interface, and more providers will be added soon.</p>
<p>Authentication integrates tightly with other Juno services, like <a href="https://juno.build/docs/build/datastore" rel="noopener noreferrer external">datastore</a> and <a href="https://juno.build/docs/build/storage" rel="noopener noreferrer external">storage</a>:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { signIn } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@junobuild/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> btn </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> document.</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">#signin</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">btn?.</span><span style="color:#50FA7B">addEventListener</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">click</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, signIn, { passive</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2"> });</span></span></code></pre><p><img src="https://daviddalbusco.com/assets/images/1*qiEtbcRsDdHpYOys37FxOQ.png" alt="Authentication screenshot" / loading="lazy"></p>
<hr />
<h2>Datastore</h2>
<p>Juno Datastore can be use to add persistence to your application using simple constructs: collections and documents.</p>
<p>Collections specify which users and/or administrators have access to their data. Juno itself does <em>not</em> have access to data in collections (unless you make it public, of course). Juno also gives you the possibility to design your architecture so that even <em>you</em> (the developer of your application) can have no access to the data saved by your users.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { getDoc, setDoc } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@junobuild/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> collection </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">my_collection</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> key </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">my_key</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> doc </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> getDoc</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    collection,</span></span>
<span class="line"><span style="color:#F8F8F2">    key</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">await</span><span style="color:#50FA7B"> setDoc</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    collection,</span></span>
<span class="line"><span style="color:#F8F8F2">    doc</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        key,</span></span>
<span class="line"><span style="color:#FF79C6">        ...</span><span style="color:#F8F8F2">doc,</span></span>
<span class="line"><span style="color:#F8F8F2">        data</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">            email</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> myNewEmail</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><p><img src="https://daviddalbusco.com/assets/images/1*q0vP9e7HGGsCpncy8LBALQ.png" alt="Datastore screenshot" / loading="lazy"></p>
<hr />
<h2>Storage</h2>
<p>Juno Storage is the easiest way to let your users upload and manage their files securely on the blockchain. The API we provide is designed to obfuscate any potential complexity. It takes advantage of the same read and write permission concept as the Datastore and automatically makes the file available on the Internet with the help of a built-in query parameters &quot;token&quot; mechanism that can be use to make users files secret.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> input </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> document.</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">input</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> { downloadUrl } </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> uploadFile</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    data</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> input.files[</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">    collection</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">images</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><p><img src="https://daviddalbusco.com/assets/images/1*Yqwt5SdUaxJqK8z_ASWn4g.png" alt="Storage screenshot" / loading="lazy"></p>
<hr />
<h2>Hosting</h2>
<p>Juno Hosting - like our other services - runs on the Internet Computer. It is the only blockchain with canister smart contracts that can serve web assets to browsers straight from the chain -- meaning no dependency on centralized Big Tech.</p>
<p>Therefore, the &quot;Satellites&quot; launched on Juno run entirely on blockchain, which allows for full decentralization.</p>
<p>Custom domain names are fully supported and easily configured, meaning your unique brand identity will be just as visible on chain.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*J3djnwgjWVWbGqYInpMMmA.png" alt="Hosting screenshot" / loading="lazy"></p>
<hr />
<h2>What's next?</h2>
<p>We are starting our closed beta program today. Contact David on <a href="https://daviddalbusco.com" rel="noopener noreferrer external">Twitter</a> or fill this <a href="https://jx5yt-yyaaa-aaaal-abzbq-cai.ic0.app/" rel="noopener noreferrer external">form</a> - built with Juno 😉 - to enter the waiting list.</p>
<p>⭐️⭐️⭐️ are also much appreciated: visit the <a href="https://github.com/junobuild/juno" rel="noopener noreferrer external">GitHub repo</a> and show your support!</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Svelte Custom Stores TypeScript Declaration]]></title>
          <description><![CDATA[Svelte offers some nice easy-to-use store concepts. One of those is the custom store but how to declare its types?]]></description>
          <link>https://daviddalbusco.com/blog/svelte-custom-stores-typescript-declaration/</link>
          <pubDate>Fri, 20 Jan 2023 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://images.unsplash.com/photo-1537237858032-3ad1b513cbcc?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwzMDkyMzV8MHwxfHNlYXJjaHwzM3x8YWJzdHJhY3R8ZW58MHx8fHwxNjc0MjAyMzA5&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080" alt="https://unsplash.com/photos/azCTGObXR14" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@jjying?utm_source=Papyrs&amp;utm_medium=referral" rel="noopener noreferrer external">JJ Ying</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>Svelte offers some nice easy-to-use store concepts. One of those is &quot;<a href="https://svelte.dev/tutorial/custom-stores" rel="noopener noreferrer external">custom stores</a>&quot; which makes handy the obfuscation of the reactive stored data and its access through a set of custom defined functions.</p>
<p>e.g. for a counter that counts apples and bananas, I can create a custom store that does not expose directly my data. Within a function, I can &quot;hide&quot; the store and expose only the functions I want to use in my apps instead of exposing publicly <code>set</code> and <code>update</code>. This to avoid misbehavior and unexpected changes, what can be really useful as the code base grows.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { writable } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">svelte/store</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> interface</span><span style="color:#8BE9FD;font-style:italic"> Counter</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    apples</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    bananas</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> initStore</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> initialCounter</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Counter</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        apples</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        bananas</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 0</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { subscribe, set, update } </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> writable</span><span style="color:#F8F8F2">(initialCounter);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        subscribe,</span></span>
<span class="line"><span style="color:#50FA7B">        incBanana</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span></span>
<span class="line"><span style="color:#50FA7B">            update</span><span style="color:#F8F8F2">(({ </span><span style="color:#FFB86C;font-style:italic">bananas</span><span style="color:#F8F8F2">, </span><span style="color:#FF79C6">...</span><span style="color:#FFB86C;font-style:italic">rest</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> ({</span></span>
<span class="line"><span style="color:#FF79C6">                ...</span><span style="color:#F8F8F2">rest,</span></span>
<span class="line"><span style="color:#F8F8F2">                bananas</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> bananas </span><span style="color:#FF79C6">+</span><span style="color:#BD93F9"> 1</span></span>
<span class="line"><span style="color:#F8F8F2">            })),</span></span>
<span class="line"><span style="color:#50FA7B">        decBanana</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span></span>
<span class="line"><span style="color:#50FA7B">            update</span><span style="color:#F8F8F2">(({ </span><span style="color:#FFB86C;font-style:italic">bananas</span><span style="color:#F8F8F2">, </span><span style="color:#FF79C6">...</span><span style="color:#FFB86C;font-style:italic">rest</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> ({</span></span>
<span class="line"><span style="color:#FF79C6">                ...</span><span style="color:#F8F8F2">rest,</span></span>
<span class="line"><span style="color:#F8F8F2">                bananas</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> bananas </span><span style="color:#FF79C6">-</span><span style="color:#BD93F9"> 1</span></span>
<span class="line"><span style="color:#F8F8F2">            })),</span></span>
<span class="line"><span style="color:#50FA7B">        reset</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> set</span><span style="color:#F8F8F2">(initialCounter)</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> counter </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> initStore</span><span style="color:#F8F8F2">();</span></span></code></pre><p>Above code snippet follows Svelte's documentation example with the addition of a type - <code>Counter</code> - for the object saved in my store and few custom functions - <code>incBanana</code>, <code>decBanana</code> and <code>reset</code>.</p>
<p><em>Note that I just did not implement the &quot;apples&quot; related functions to keep the snippet small. In a real use case I might have even extracted the <code>update()</code> part to a function to avoid double code but, I also skipped this part because it would have not much value in this blog post context.</em></p>
<p>When bind in a component, it works as expected.</p>
<p><img src="https://6zvwc-sqaaa-aaaal-aalma-cai.raw.ic0.app/images/counter.gif?token=zL0w-s4NX-jmjm6Oc6Ci3" alt="counter.gif" / loading="lazy"></p>
<p>However, even if <code>npm run dev|build|check</code> are all fine, Webstorm is not being able to derive the store values when used in a TypeScript <code>script</code> block.</p>
<p><img src="https://6zvwc-sqaaa-aaaal-aalma-cai.raw.ic0.app/images/capture-d%E2%80%99e%CC%81cran-2023-01-20-a%CC%80-10.31.09.png?token=ptBGQR8Uj4TMUJPSCei7Z" alt="capture-d%E2%80%99e%CC%81cran-2023-01-20-a%CC%80-10.31.09.png" / loading="lazy"></p>
<p>I do not know exactly if it is yet another issue of the Jetbrains Svelte plugin or if the store is derived as <code>any</code> type by the bundler (which would ignore the <code>strict</code> option 🤷‍♂️). Nevertheless, I rather like to fix the issue by improving the type declaration of the store.</p>
<p>Because my custom store exposes my home made functions but, also the root <code>subscribe</code> feature, its declaration has to providing support for it as well.</p>
<p>Therefore the most easy way in my opinion is to inherits the existing <code>Readable</code> type of Svelte.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { </span><span style="color:#FF79C6">type</span><span style="color:#F8F8F2"> Readable } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">svelte/store</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> interface</span><span style="color:#8BE9FD;font-style:italic"> AuthStore</span><span style="color:#FF79C6"> extends</span><span style="color:#8BE9FD;font-style:italic"> Readable</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Counter</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#50FA7B">    incBanana</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#8BE9FD;font-style:italic"> void</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#50FA7B">    decBanana</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#8BE9FD;font-style:italic"> void</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#50FA7B">    reset</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#8BE9FD;font-style:italic"> void</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>By setting the generic types to my custom data type - <code>Counter</code> - I can instruct which type is handled in the store and by declaring my functions, I can improve the typings.</p>
<p>I can use this new interface to set the return type of the initialization function - <code>initStore</code> - and that's already it.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { writable, </span><span style="color:#FF79C6">type</span><span style="color:#F8F8F2"> Readable } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">svelte/store</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> interface</span><span style="color:#8BE9FD;font-style:italic"> Counter</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    apples</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    bananas</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> interface</span><span style="color:#8BE9FD;font-style:italic"> AuthStore</span><span style="color:#FF79C6"> extends</span><span style="color:#8BE9FD;font-style:italic"> Readable</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Counter</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#50FA7B">    incBanana</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#8BE9FD;font-style:italic"> void</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#50FA7B">    decBanana</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#8BE9FD;font-style:italic"> void</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#50FA7B">    reset</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#8BE9FD;font-style:italic"> void</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> initStore</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> AuthStore</span><span style="color:#FF79C6"> =></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> initialCounter</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Counter</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        apples</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        bananas</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 0</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { subscribe, set, update } </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> writable</span><span style="color:#F8F8F2">(initialCounter);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        subscribe,</span></span>
<span class="line"><span style="color:#50FA7B">        incBanana</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span></span>
<span class="line"><span style="color:#50FA7B">            update</span><span style="color:#F8F8F2">(({ </span><span style="color:#FFB86C;font-style:italic">bananas</span><span style="color:#F8F8F2">, </span><span style="color:#FF79C6">...</span><span style="color:#FFB86C;font-style:italic">rest</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> ({</span></span>
<span class="line"><span style="color:#FF79C6">                ...</span><span style="color:#F8F8F2">rest,</span></span>
<span class="line"><span style="color:#F8F8F2">                bananas</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> bananas </span><span style="color:#FF79C6">+</span><span style="color:#BD93F9"> 1</span></span>
<span class="line"><span style="color:#F8F8F2">            })),</span></span>
<span class="line"><span style="color:#50FA7B">        decBanana</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span></span>
<span class="line"><span style="color:#50FA7B">            update</span><span style="color:#F8F8F2">(({ </span><span style="color:#FFB86C;font-style:italic">bananas</span><span style="color:#F8F8F2">, </span><span style="color:#FF79C6">...</span><span style="color:#FFB86C;font-style:italic">rest</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> ({</span></span>
<span class="line"><span style="color:#FF79C6">                ...</span><span style="color:#F8F8F2">rest,</span></span>
<span class="line"><span style="color:#F8F8F2">                bananas</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> bananas </span><span style="color:#FF79C6">-</span><span style="color:#BD93F9"> 1</span></span>
<span class="line"><span style="color:#F8F8F2">            })),</span></span>
<span class="line"><span style="color:#50FA7B">        reset</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> set</span><span style="color:#F8F8F2">(initialCounter)</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> counter </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> initStore</span><span style="color:#F8F8F2">();</span></span></code></pre><p>When I open my component, I notice that Webstorm now understands correctly the type of my data.</p>
<p><img src="https://6zvwc-sqaaa-aaaal-aalma-cai.raw.ic0.app/images/capture-d%E2%80%99e%CC%81cran-2023-01-20-a%CC%80-10.42.55.png?token=JvVSu9s4WjDS7Eyq0YxE2" alt="capture-d%E2%80%99e%CC%81cran-2023-01-20-a%CC%80-10.42.55.png" / loading="lazy"></p>
<p>To infinity and beyond<br />
David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Product naming is hard 😩]]></title>
          <description><![CDATA[Gosh, I finally got the name for my new side project. It will be called: Juno.]]></description>
          <link>https://daviddalbusco.com/blog/product-naming-is-hard/</link>
          <pubDate>Tue, 10 Jan 2023 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/0*ySGfMIBPjKjuxUC9.jpg" alt="becca-tapert-gny_mw1q6xc-unsplash.jpg" / loading="lazy"></p>
<p><em>Photo de <a href="https://unsplash.com/@beccatapert?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Becca Tapert</a> sur <a href="https://unsplash.com/fr/photos/GnY_mW1Q6Xc?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>Gosh, I finally got the name for my new side project. It will be called: <strong><a href="https://twitter.com/junobuild" rel="noopener noreferrer external">Juno</a></strong>.</p>
<hr />
<h2>&quot;Nervous breakdown&quot;</h2>
<p>Starting a blog post with such a title is of course way exaggerated but, daaaaamn it was not easy to come up with a name.</p>
<p>I started working on my new side project two months ago roughly (Nov. 13 2022) and as soon as I validated my proof of concept, I started thinking at potential brand or product names.</p>
<p>It took me a all month of brainstorming to finally opt for a name.</p>
<p>What a frustrating phase of development for the project. Not finding - and owning - a project name really felt like going no where with it.</p>
<p>That's why, needless to say, I feel now relieved. So, here are some super high level marketing hints and the story behind <strong>Juno</strong>'s name.</p>
<hr />
<h2>My marketing</h2>
<p>Most of following points might be obvious to the one who's searching for a product name or have better knowledges in marketing than myself but, who knows. It might be useful to someone, some day 🤷‍♂️.</p>
<hr />
<h3>1. Goal</h3>
<p>Knowing the exact goal and intent of a project is my starting point. From there, I can tackle other topics but, I first need to be clear on what I want to develop, offer or more important: what is the real problem I want to solve?</p>
<p>In my new project particular case, I aim to make the development of applications on web3 more accessible for all web2 developers of the world.</p>
<p>On one side, I believe such technology leads to better data's ownership and project governance, what per extension is beneficial for end users.</p>
<p>On the other, by being active on various channel to support beginners, I notice that starting in such a field can be still tough. It may even discourage some newcomers to not go further in their projects of decentralization and blockchain.</p>
<p>Therefore soon, I will ease - by a lot - building decentralized applications and this, for the millions of JavaScript frontend programmers.</p>
<p>That's my goal. <strong>Juno</strong> is a game changer!</p>
<hr />
<h3>2. Target audience</h3>
<p>It is important to know who are the final clients, prospects or users of a product to address them in the most suitable way.</p>
<p>e.g. if I would create an e-learning platform targeting 70+ years old persons, I would refer to my mum and dad for examples. I would adjust the way I communicate accordingly but, also integrate this in the equation to pick a product name.</p>
<p>Indeed, if I would launch a platform that uses a non-common top domain - such as <code>.com</code> or <code>.ch</code> - I know my parents would be lost and skeptical about it.</p>
<p>Likewise, if I would use an english word they do not know for naming purpose, I know they would need some explanation to understand it.</p>
<p>The more questions, the more scepticism, the more barrier to entry, the less conversions - i.e. the less chance to attract users.</p>
<p>In case of <strong>Juno</strong>, I am addressing developers which makes things a bit easier because, I am part of that target audience. That said, it is not always an asset to consider her/himself as such. It can lead to shortcuts and misjudgements - e.g. like assuming all other developers develop the same way I do. However, in this particular case, I think it helps (🤞).</p>
<hr />
<h3>3. Competitors</h3>
<p>A competitor analysis - comprehensive or even bear minimal - is useful to get to know what strategy and target might work the best to maximize the conversions but, it also helps to get to know where the new product would fit in the market, particularly if the segments already existing.</p>
<p>It is also useful to know what the competitors do in term of naming. It can be interesting to apply a similar or diametrically opposite strategy.</p>
<p>As I am developing a BaaS - a Blockchain-as-a-Service - I first looked at the actors of such industry.</p>
<p>There are few API platforms - e.g. Moralis or QuickNode - which are similar in a way that they try to make dapps development effortless too but, none so far are running integrally on chain nor seem to be open source.</p>
<p>That is why, I mostly compared my idea with web2 BaaS - Backend-as-a-Service. Their offering and target audience are, to some extent, more similar to mine.</p>
<p>Among these, I can list examples such as Firebase, Supabase, Hasura, Netlify, Appwrite, AWS Amplify, Vercel etc.</p>
<p>What I learned from that research is that they all use short names - &quot;obviously&quot; you might say - which most often do not contain explicit keywords such as &quot;App&quot; or &quot;Dev&quot;.</p>
<p>They do so too, I am guessing, because developers install and run their command line tools on their laptop and continuous integration platforms. Therefore, the shorter the command, the more easiest the name to remember.</p>
<p>I think <strong>Juno</strong> works well 😁.</p>
<hr />
<h3>4. Storytelling</h3>
<p>A brand story is a summary of its history, mission, purpose, and values, with a narrative structure that brings it to life.</p>
<p>Using brand storytelling can create a series of plot points and a rising action that generate an emotional connection between the brand and its target audience (<a href="https://www.semrush.com/blog/definitive-guide-to-brand-storytelling/?kw=&amp;cmp=FR_SRCH_DSA_Blog_EN&amp;label=dsa_pagefeed&amp;Network=g&amp;Device=c&amp;utm_content=622456726686&amp;kwid=dsa-1754979160045&amp;cmpid=18361911540&amp;agpid=141830026056&amp;BU=Core&amp;extid=60109562455&amp;adpos=&amp;gclid=EAIaIQobChMIwPWl-oC4_AIVBY9oCR14dgOGEAAYASAAEgI9MvD_BwE" rel="noopener noreferrer external">source</a>).</p>
<p>This blog post is storytelling 😇.</p>
<p>The development of my projects includes various smart contracts. Notably one that will act as a main (1) platform, one per user dedicated to the administration (2) and hopefully multiple per user (3), as many as they launch projects.</p>
<p>What led me to the idea of using terms related to the universe of the &quot;space&quot;. This to identify the various smart contracts.</p>
<p>The main (1) one could be called &quot;console&quot;, the user admin (2) could be a &quot;mission control&quot; and each projects (3) could become &quot;satellite&quot; or maybe even later on &quot;rocket&quot; or &quot;space station&quot;.</p>
<p>With such a context, I am able to establish a comprehensive narrative. From the code, till the documentation to the presentation material, a all story that can be relatively easily followed and understood.</p>
<p>Of course, I was not the first one that thought about using such a topic as &quot;space&quot; to create a brand. That is probably why it took me so long to finally find something available.</p>
<p>I am glad I finally figured out a solution. There was the Apollo program, there is now the <strong>Juno</strong> program 😁.</p>
<p>_<a href="https://en.wikipedia.org/wiki/Juno_(mythology)" rel="noopener noreferrer external">Juno</a> is an ancient Roman goddess. She was equated to Hera, goddess of the sky in Greek mythology._</p>
<hr />
<h3>5. Keywords</h3>
<p>I totally skipped that part but, as a diligent aspirant indie hacker, I should have thought about making some research on keywords. I should have try some combinations of keywords in Google trends to validate the potential names, including <strong>Juno</strong>.</p>
<p>By finding a name that matches lots of research or which might happen to be trending can lead to attract more curiosity and per extension maybe more conversions.</p>
<p>Fortunately, the name itself is not the only variable in this equation. All the static content - such as the documentation or blog posts - I will produce also matters for SEO purpose. I still got a change here 😜.</p>
<p>Nevertheless, next time I am looking for a product name, I should think about making such types of analysis.</p>
<hr />
<h3>6. Similarity</h3>
<p>Let's face it, nowadays it is almost impossible to come up with a (short) name that has never, ever, been used on the Internet and there is a good chance that we fell in love with a name that is already used.</p>
<p>I am no lawyer nor patent property expert, that is why when I search for a product name and really like one, I just try my best to pick one that at least does not match any in the exact same context.</p>
<p>e.g. when I decided to go for <strong>Juno</strong>, I was aware such a name was already used by a company that sells Microsoft related services but, I was actually not aware there was also a crypto bank related project. I also did not found other projects that use the term but, cannot rule it out.</p>
<p>That is one of the few reasons why I also opted for a top domain - <code>.build</code> - that has a explicit meaning. It will address developers that want to build projects - i.e. it strengthens the purpose of the name.</p>
<hr />
<h3>7. Channels</h3>
<p>It is probably common to know that in addition to finding a domain name that is not yet used, it is also worth checking for usernames' availability on social platforms (Twitter etc.).</p>
<p>However, it is maybe less common to known, particularly for non-tech people, that the same rules apply for code delivery channels.</p>
<p>Indeed, particularly if the project is open source, you have to find an available name for a project or organization on the Internet hosting service where you will store your code and issues tracker. Commonly on GitHub.</p>
<p>Likewise, if your project provides libraries or other command line tools, you will also have to reserve an identifier on the packet manager that you will use to ship. Commonly in JavaScript npm.</p>
<p>Again here, most names are already taken. In addition, while GitHub used to released inactive names, they recently stopped to do so (since April 2022 I think). It means that you cannot even target anymore a name that looks inactive. You really have to come up with a unique new name or a uncool variant - like adding an underscore before or after your product name 😥.</p>
<p>Few solutions and lots of restriction but, I think I actually managed to find some a-ok (🙂) channels for <strong>Juno</strong>:</p>
<ul>
<li><p><a href="https://juno.build/" rel="noopener noreferrer external">https://juno.build/</a></p></li>
<li><p><a href="https://twitter.com/junobuild" rel="noopener noreferrer external">https://twitter.com/junobuild</a></p></li>
<li><p><a href="https://github.com/buildwithjuno" rel="noopener noreferrer external">https://github.com/buildwithjuno</a></p></li>
<li><p><a href="https://www.npmjs.com/org/junobuild" rel="noopener noreferrer external">https://www.npmjs.com/org/junobuild</a></p></li>
</ul>
<hr />
<h1>Conclusion</h1>
<p>Finding a product name is hard. It might be &quot;just&quot; a name and the market might hate it or love it but, it is &quot;your&quot; name. The one you picked for the rest of your life (even if you are aware you can rebrand anytime 😉).</p>
<p>It is an important step to move a project forward, build the storytelling and unlock other tasks.</p>
<p>When it comes to mine, I have now contacted a designer friend of mine - with whom I often collaborated, most recently on <a href="https://papy.rs" rel="noopener noreferrer external">Papy.rs</a> - and we began thinking at a logo 👨‍🎨.</p>
<p>The programming is almost ready, I tackled all major features for starting a beta version. It just needs a bit of fine tuning but, yes it is ready.</p>
<p>Except hearing more and more about it in following weeks!</p>
<p>To <a href="https://twitter.com/junobuild" rel="noopener noreferrer external">Juno</a> and beyond 🚀<br />
David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[TypeScript: isNullish, nonNullish and assertNonNullish]]></title>
          <description><![CDATA[Three little TypeScript assertion functions that have proven to be really useful.]]></description>
          <link>https://daviddalbusco.com/blog/typescript-isnullish-nonnullish-and-assertnonnullish/</link>
          <pubDate>Fri, 30 Dec 2022 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://images.unsplash.com/photo-1630083133034-b4644df639cf?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwzMDkyMzV8MHwxfHNlYXJjaHw4fHxuaWx8ZW58MHx8fHwxNjcyNDAzODEy&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080" alt="Nil in the Alps" / loading="lazy"></p>
<p>_<a href="https://unsplash.com/fr/@shoots_of_bapt_" rel="noopener noreferrer external">Baptiste Buisson</a>_</p>
<p>There are few gems we have implemented in <a href="https://nns.ic0.app/" rel="noopener noreferrer external">NNS-dapp</a> that makes our devs life easier on daily basis. Among those, the following three little <a href="https://www.typescriptlang.org/" rel="noopener noreferrer external">TypeScript</a> functions have proven to be really useful.</p>
<hr />
<h2>isNullish</h2>
<p>How often have you code <code>if...else</code> statement that checks if an object is <code>undefined</code> or <code>null</code>?</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">// Pseudo code (assuming optional chaining does not exist 😉)</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> test</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">obj</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> MyObject</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (obj </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> undefined</span><span style="color:#FF79C6"> ||</span><span style="color:#F8F8F2"> obj </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> null</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    obj.</span><span style="color:#50FA7B">fn</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Thanks to <a href="https://www.typescriptlang.org/docs/handbook/2/generics.html" rel="noopener noreferrer external">generics</a> and <a href="https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates" rel="noopener noreferrer external">type predicates</a>, we have developed a helper that makes us avoid code duplication while preserving type safety.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> isNullish</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">>(</span><span style="color:#FFB86C;font-style:italic">argument</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> T</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#FFB86C;font-style:italic"> argument</span><span style="color:#FF79C6"> is</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#FF79C6"> =></span></span>
<span class="line"><span style="color:#F8F8F2">    argument </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> null</span><span style="color:#FF79C6"> ||</span><span style="color:#F8F8F2"> argument </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span></code></pre><p>The use of a generic <code>T</code> will scope the usage of the function to the types we have declared in our project.</p>
<p>As for the type guard, it narrows TypeScript to understand that the variable is indeed from the specific expected type. In other words, this function makes TypeScript understands that the parameter is - if it matches the checks of the function - indeed <code>undefined</code> or <code>null</code>.</p>
<p>We can use the helper as following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> test</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">obj</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> MyObject</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#6272A4">    // 1. Avoid code duplication</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#50FA7B">isNullish</span><span style="color:#F8F8F2">(obj)) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // 2. TypeScript checks it is defined</span></span>
<span class="line"><span style="color:#F8F8F2">    obj.</span><span style="color:#50FA7B">fn</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><hr />
<h2>nonNullish</h2>
<p>Sometimes we need the opposite, we need to know if something is defined. While we can negate previous shorthand function to get to know if it is the case, we also want to preserve the type safety.</p>
<p>This can be achieved by using the utility <a href="https://www.typescriptlang.org/docs/handbook/utility-types.html#nonnullabletype" rel="noopener noreferrer external">NonNullable</a> to construct a type by excluding <code>undefined</code> and <code>null</code>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> nonNullish</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">>(</span><span style="color:#FFB86C;font-style:italic">argument</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> T</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#FFB86C;font-style:italic"> argument</span><span style="color:#FF79C6"> is</span><span style="color:#8BE9FD;font-style:italic"> NonNullable</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=></span></span>
<span class="line"><span style="color:#FF79C6">    !</span><span style="color:#50FA7B">isNullish</span><span style="color:#F8F8F2">(argument);</span></span></code></pre><p>That way, TypeScript will understand that indeed, an object is defined.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> test</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">obj</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> MyObject</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#6272A4">    //1. Avoid code duplication</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#50FA7B">nonNullish</span><span style="color:#F8F8F2">(obj)) {</span></span>
<span class="line"><span style="color:#6272A4">        // 2. TypeScript checks it is defined</span></span>
<span class="line"><span style="color:#F8F8F2">        obj.</span><span style="color:#50FA7B">fn</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><hr />
<h2>assertNonNullish</h2>
<p>In addition to checking if specified conditions are truthy, throwing errors if these are falsy can be handy. Notably for the development of guards with assertion patterns.</p>
<p>For such purpose, we can enhance the functions we have previously developed with the <a href="https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html" rel="noopener noreferrer external">assertion signatures</a> concept that has been introduced in TypeScript 3.7.</p>
<p>Using the <code>asserts</code> keyword and a condition we can make TypeScript aware that a helper will perform a check and throw an error if conditions are not met.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> NullishError</span><span style="color:#FF79C6"> extends</span><span style="color:#8BE9FD;font-style:italic"> Error</span><span style="color:#F8F8F2"> {}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> assertNonNullish</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">>(</span></span>
<span class="line"><span style="color:#FFB86C;font-style:italic">    value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> T</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#FFB86C;font-style:italic">    message</span><span style="color:#FF79C6">?:</span><span style="color:#8BE9FD;font-style:italic"> string</span></span>
<span class="line"><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#FF79C6"> asserts</span><span style="color:#FFB86C;font-style:italic"> value</span><span style="color:#FF79C6"> is</span><span style="color:#8BE9FD;font-style:italic"> NonNullable</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">>(</span><span style="color:#FFB86C;font-style:italic">value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> T</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">message</span><span style="color:#FF79C6">?:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> void</span><span style="color:#FF79C6"> =></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#50FA7B">isNullish</span><span style="color:#F8F8F2">(value)) {</span></span>
<span class="line"><span style="color:#FF79C6">        throw</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> NullishError</span><span style="color:#F8F8F2">(message);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Applied to our previous code snippet, we can transform the function to execute the code only if the guard is matching.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> test</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">obj</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> MyObject</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#6272A4">    // 1. Avoid code duplication</span></span>
<span class="line"><span style="color:#6272A4">    // 2. TypeScript understands it might throw an error</span></span>
<span class="line"><span style="color:#50FA7B">    assertNonNullish</span><span style="color:#F8F8F2">(obj);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // 3. TypeScript checks it is defined</span></span>
<span class="line"><span style="color:#F8F8F2">    obj.</span><span style="color:#50FA7B">fn</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><hr />
<h2>Conclusion</h2>
<p>I find these helpers so useful that I now use these in any of my recent works - including my new &quot;secret crazy&quot; side project - and I bet you will do too 😁.</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Sass media queries mixins]]></title>
          <description><![CDATA[A Sass media queries mixin to avoid duplicating CSS breakpoints all over the place.]]></description>
          <link>https://daviddalbusco.com/blog/sass-media-queries-mixins/</link>
          <pubDate>Thu, 22 Dec 2022 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://images.unsplash.com/photo-1486927181919-3ac1fc3a8082?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwzMDkyMzV8MHwxfHNlYXJjaHwyOHx8bGluZXxlbnwwfHx8fDE2NzE3MTI0MjM&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080" alt="ELNÒS Shopping mall pattern" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@lucabravo?utm_source=Papyrs&amp;utm_medium=referral" rel="noopener noreferrer external">Luca Bravo</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>There are few gems we have implemented in <a href="https://nns.ic0.app/" rel="noopener noreferrer external">NNS-dapp</a> that makes our devs life easier on daily basis. One of these has been implemented my colleague <a href="https://github.com/mstrasinskis" rel="noopener noreferrer external">Maskims Strasinskis</a>: a <a href="https://sass-lang.com/" rel="noopener noreferrer external">Sass</a> media queries <a href="https://sass-lang.com/documentation/at-rules/mixin" rel="noopener noreferrer external">mixins</a> that makes us avoid duplicating CSS breakpoints all over the place.</p>
<hr />
<h2>Foreword</h2>
<p><a href="https://developer.mozilla.org/fr/docs/Web/CSS/Media_Queries/Using_media_queries" rel="noopener noreferrer external">CSS media queries</a> are handy but, as the app or design system grows, their usage grows as well exponentially which ultimately, makes the UI hard to maintain and difficult to refactor or re-work.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">@media</span><span style="color:#F8F8F2"> (</span><span style="color:#8BE9FD">min-width</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 300</span><span style="color:#FF79C6">px</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#8BE9FD">    background</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> red</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Imagine having not one but, hundred of above breakpoints in your zillion of CSS files. If one day the design changes, you might have to run a gigantic search and replace to adapt your code.</p>
<p>That's where my colleague tricks becomes handy.</p>
<hr />
<h2>Mixins</h2>
<p>Sass mixins allow you to define styles that can be re-used throughout your stylesheet. They make it easy to avoid using non-semantic classes, and to distribute collections of styles in libraries (<a href="https://sass-lang.com/documentation/at-rules/mixin" rel="noopener noreferrer external">source</a>).</p>
<p>In other words, mixins are functions that can be used to replicate the same styles multiple times in your apps or libs without the need to duplicate the code.</p>
<p>In this way, the idea is to create a function that generates the breakpoints but obfuscate their values. That's why, the solution begins with the definition of Sass variables which define these sizes - e.g. the following within a <code>\_media.scss</code> file.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">$breakpoint-xsmall</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 320</span><span style="color:#FF79C6">px</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">$breakpoint-small</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 576</span><span style="color:#FF79C6">px</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">$breakpoint-medium</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 768</span><span style="color:#FF79C6">px</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">$breakpoint-large</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 1024</span><span style="color:#FF79C6">px</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">$breakpoint-extra-large</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 1300</span><span style="color:#FF79C6">px</span><span style="color:#F8F8F2">;</span></span></code></pre><p>Once the breakpoints defined, within the same file we can implement the effective mixins that maps a shorthand variable - e.g. <code>small</code> - with the size that we declared once (previous code).</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">@mixin</span><span style="color:#50FA7B"> min-width</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">$breakpoint</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">    @if</span><span style="color:#F8F8F2"> ($breakpoint == xsmall) {</span></span>
<span class="line"><span style="color:#FF79C6">        @media</span><span style="color:#F8F8F2"> (</span><span style="color:#8BE9FD">min-width</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> $breakpoint-xsmall) {</span></span>
<span class="line"><span style="color:#FF79C6">            @content</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">@else if</span><span style="color:#F8F8F2"> ($breakpoint == </span><span style="color:#BD93F9">small</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        @media</span><span style="color:#F8F8F2"> (</span><span style="color:#8BE9FD">min-width</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> $breakpoint-small) {</span></span>
<span class="line"><span style="color:#FF79C6">            @content</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">@else if</span><span style="color:#F8F8F2"> ($breakpoint == </span><span style="color:#BD93F9">medium</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        @media</span><span style="color:#F8F8F2"> (</span><span style="color:#8BE9FD">min-width</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> $breakpoint-medium) {</span></span>
<span class="line"><span style="color:#FF79C6">            @content</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">@else if</span><span style="color:#F8F8F2"> ($breakpoint == </span><span style="color:#BD93F9">large</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        @media</span><span style="color:#F8F8F2"> (</span><span style="color:#8BE9FD">min-width</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> $breakpoint-large) {</span></span>
<span class="line"><span style="color:#FF79C6">            @content</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">@else if</span><span style="color:#F8F8F2"> ($breakpoint == xlarge) {</span></span>
<span class="line"><span style="color:#FF79C6">        @media</span><span style="color:#F8F8F2"> (</span><span style="color:#8BE9FD">min-width</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> $breakpoint-extra-large) {</span></span>
<span class="line"><span style="color:#FF79C6">            @content</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">@else </span><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#FF79C6">        @error</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">UNKNOWN MEDIA BREAKPOINT </span><span style="color:#FF79C6">#{</span><span style="color:#F8F8F2">$breakpoint</span><span style="color:#FF79C6">}</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>In addition to taking arguments, a mixin can take an entire block of styles, known as a content block (<a href="https://sass-lang.com/documentation/at-rules/mixin#content-blocks" rel="noopener noreferrer external">source</a>). Content which we can render or not according the matching conditions.</p>
<hr />
<h2>Usage</h2>
<p>Once the mixins set, it can be loaded with a <a href="https://sass-lang.com/documentation/at-rules/use" rel="noopener noreferrer external">use</a> rule and included as following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">style</span><span style="color:#50FA7B;font-style:italic"> lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">scss</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#FF79C6">    @use</span><span style="color:#F8F8F2"> './styles/mixins/media';</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    div</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        @include media.</span><span style="color:#8BE9FD">min-width</span><span style="color:#F8F8F2">(small) {</span></span>
<span class="line"><span style="color:#8BE9FD">            background</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> red</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        @include</span><span style="color:#F8F8F2"> media.min-width(large) {</span></span>
<span class="line"><span style="color:#FF79C6">            background: green;</span></span>
<span class="line"><span style="color:#FF79C6">        }</span></span>
<span class="line"><span style="color:#FF79C6">    }</span></span>
<span class="line"><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2">/</span><span style="color:#FF79C6">script></span></span></code></pre><p>And voilà, it's ready to serve 👨‍🍳.</p>
<hr />
<h2>Conclusion</h2>
<p>I should try to write a bit more about all these utilities and other handy features we have developed with my colleagues this year while re-building entirely <a href="https://nns.ic0.app/" rel="noopener noreferrer external">NNS-dapp</a> from the ground-up and also making it evolves, a lot. Even though the app is gigantic (e.g. we have 1'900+ jest tests running to validate each build), it's enjoyable to develop new features and easy to maintain.</p>
<p>Merry Xmas 🎄<br />
David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Canister guard in Rust on the Internet Computer]]></title>
          <description><![CDATA[Guard functions can be executed before updates and queries of canister smart contracts written in Rust on the Internet Computer.]]></description>
          <link>https://daviddalbusco.com/blog/canister-guard-in-rust/</link>
          <pubDate>Thu, 15 Dec 2022 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://images.unsplash.com/photo-1571283056653-e9802feac258?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwzMDkyMzV8MHwxfHNlYXJjaHwzMnx8Z3VhcmR8ZW58MHx8fHwxNjcxMTE1ODg1&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080" alt="Security from a Cypress Towne Lake event enjoyed the 4th of July fireworks with the rest of the crowd." / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@illuminationmarketing?utm_source=Papyrs&amp;utm_medium=referral" rel="noopener noreferrer external">Illumination Marketing</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<hr />
<p>I recently discovered it was possible to specify a guard function to be executed before update and query functions of canister smart contracts written in Rust on the <a href="https://internetcomputer.org/" rel="noopener noreferrer external">Internet Computer</a>.</p>
<p>You might tell me that the following post is just a rip off of the <a href="https://docs.rs/ic-cdk/latest/ic_cdk/attr.update.html" rel="noopener noreferrer external">Crate</a> documentation but, as I only figured out this was possible while having a look at the <a href="https://github.com/open-ic/transaction-notifier" rel="noopener noreferrer external">transaction notifier</a> repo of <a href="https://oc.app/" rel="noopener noreferrer external">OpenChat</a>, I thought it was worth a post 😄.</p>
<hr />
<h2>Original approach</h2>
<p>I began my journey with Rust when I migrated my existing Motoko code - i.e. when I upgraded the existing smart contracts of <a href="https://papy.rs" rel="noopener noreferrer external">Papyrs</a>.</p>
<p>As these canisters were dedicated to user data, I had to migrate functions that required access control too.</p>
<p>For this purpose, I implemented comparison of principals - i.e. I match the callers of functions against users that are saved in the state. If they are equals, methods can be executed, if not, I throw errors and reject the call.</p>
<p>Not knowing how to write guards, I basically duplicated <code>if</code> all around the place in every calls that needed to be protected.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">use</span><span style="color:#F8F8F2"> candid</span><span style="color:#FF79C6">::</span><span style="color:#8BE9FD;font-style:italic">Principal</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">use</span><span style="color:#F8F8F2"> ic_cdk</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">{caller, trap};</span></span>
<span class="line"><span style="color:#FF79C6">use</span><span style="color:#F8F8F2"> ic_cdk_macros</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">{query, init};</span></span>
<span class="line"><span style="color:#FF79C6">use</span><span style="color:#F8F8F2"> std</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">cell</span><span style="color:#FF79C6">::</span><span style="color:#8BE9FD;font-style:italic">RefCell</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">#[derive(</span><span style="color:#8BE9FD;font-style:italic">Default</span><span style="color:#F8F8F2">)]</span></span>
<span class="line"><span style="color:#FF79C6">pub</span><span style="color:#FF79C6"> struct</span><span style="color:#8BE9FD;font-style:italic"> State</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    pub</span><span style="color:#F8F8F2"> user</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Option</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">Principal</span><span style="color:#F8F8F2">>,</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// This canister cannot be created without user</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">#[init]</span></span>
<span class="line"><span style="color:#FF79C6">fn</span><span style="color:#50FA7B"> init</span><span style="color:#F8F8F2">(user</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Principal</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#BD93F9">    STATE</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">with</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">|</span><span style="color:#F8F8F2">state</span><span style="color:#FF79C6">|</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        *</span><span style="color:#F8F8F2">state</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">borrow_mut</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=</span><span style="color:#8BE9FD;font-style:italic"> State</span><span style="color:#F8F8F2"> { user</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Some</span><span style="color:#F8F8F2">(user) };</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// Mutable global state.</span></span>
<span class="line"><span style="color:#6272A4">// See Roman Kashitsyn's post for more info:</span></span>
<span class="line"><span style="color:#6272A4">// https://mmapped.blog/posts/01-effective-rust-canisters.html</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">thread_local!</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    static</span><span style="color:#BD93F9"> STATE</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> RefCell</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">State</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=</span><span style="color:#8BE9FD;font-style:italic"> RefCell</span><span style="color:#FF79C6">::</span><span style="color:#50FA7B">default</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">#[query]</span></span>
<span class="line"><span style="color:#FF79C6">fn</span><span style="color:#50FA7B"> greet</span><span style="color:#F8F8F2">(name</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> String</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> String</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> user</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Principal</span><span style="color:#FF79C6"> =</span><span style="color:#BD93F9"> STATE</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">with</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">|</span><span style="color:#F8F8F2">state</span><span style="color:#FF79C6">|</span><span style="color:#F8F8F2"> state</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">borrow</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">.</span><span style="color:#F8F8F2">user)</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">unwrap</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // 🖖 Here I check if the caller matches the user who owns the canister</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> user </span><span style="color:#FF79C6">!=</span><span style="color:#50FA7B"> caller</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#50FA7B">        trap</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">"User does not have the permission to say hello."</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    format!</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">"Hello, {}!"</span><span style="color:#F8F8F2">, name)</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>While it works as I expected, you can easily imagine that duplicating the same code - particularly as the number of functions grows - slowly bloated the readability.</p>
<p>That was before I fortunately discovered the <strong>guard</strong> feature 💪.</p>
<hr />
<h2>Guard</h2>
<p>Out of the box, guards are functions that can be executed before an update or query function. When these returns an error, the related method is not proceed.</p>
<p>So I changed my code to take advantage of it and avoid duplication.</p>
<p>I created a new <code>guards.rs</code> module to execute the exact same pattern matching as the one I implemented in above code snippet.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">use</span><span style="color:#F8F8F2"> candid</span><span style="color:#FF79C6">::</span><span style="color:#8BE9FD;font-style:italic">Principal</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">use</span><span style="color:#F8F8F2"> ic_cdk</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">caller;</span></span>
<span class="line"><span style="color:#FF79C6">use</span><span style="color:#FF79C6"> crate::</span><span style="color:#8BE9FD;font-style:italic">STATE</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">pub</span><span style="color:#FF79C6"> fn</span><span style="color:#50FA7B"> caller_is_user</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> Result</span><span style="color:#F8F8F2">&#x3C;(), </span><span style="color:#8BE9FD;font-style:italic">String</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> caller </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> caller</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> user</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Principal</span><span style="color:#FF79C6"> =</span><span style="color:#BD93F9"> STATE</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">with</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">|</span><span style="color:#F8F8F2">state</span><span style="color:#FF79C6">|</span><span style="color:#F8F8F2"> state</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">borrow</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">.</span><span style="color:#F8F8F2">user)</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">unwrap</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> caller </span><span style="color:#FF79C6">==</span><span style="color:#F8F8F2"> user {</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">        Ok</span><span style="color:#F8F8F2">(())</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">else</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">        Err</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">"Caller is not the user of the canister."</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">to_string</span><span style="color:#F8F8F2">())</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Once set, I then &quot;just&quot; replaced my existing pseudo tests with the declaration of the guard.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">// ️🖖1️⃣ declare the new module</span></span>
<span class="line"><span style="color:#FF79C6">mod</span><span style="color:#F8F8F2"> guards;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">use</span><span style="color:#F8F8F2"> candid</span><span style="color:#FF79C6">::</span><span style="color:#8BE9FD;font-style:italic">Principal</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">use</span><span style="color:#F8F8F2"> ic_cdk</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">{caller, trap};</span></span>
<span class="line"><span style="color:#FF79C6">use</span><span style="color:#F8F8F2"> ic_cdk_macros</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">{query, init};</span></span>
<span class="line"><span style="color:#FF79C6">use</span><span style="color:#F8F8F2"> std</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">cell</span><span style="color:#FF79C6">::</span><span style="color:#8BE9FD;font-style:italic">RefCell</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#6272A4">// 🖖2️⃣ the function needs to be imported</span></span>
<span class="line"><span style="color:#FF79C6">use</span><span style="color:#FF79C6"> crate::</span><span style="color:#F8F8F2">guards</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">caller_is_user;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">#[derive(</span><span style="color:#8BE9FD;font-style:italic">Default</span><span style="color:#F8F8F2">)]</span></span>
<span class="line"><span style="color:#FF79C6">pub</span><span style="color:#FF79C6"> struct</span><span style="color:#8BE9FD;font-style:italic"> State</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    pub</span><span style="color:#F8F8F2"> user</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Option</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">Principal</span><span style="color:#F8F8F2">>,</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// This canister cannot be created without user</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">#[init]</span></span>
<span class="line"><span style="color:#FF79C6">fn</span><span style="color:#50FA7B"> init</span><span style="color:#F8F8F2">(user</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Principal</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#BD93F9">    STATE</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">with</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">|</span><span style="color:#F8F8F2">state</span><span style="color:#FF79C6">|</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        *</span><span style="color:#F8F8F2">state</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">borrow_mut</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=</span><span style="color:#8BE9FD;font-style:italic"> State</span><span style="color:#F8F8F2"> { user</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Some</span><span style="color:#F8F8F2">(user) };</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// Mutable global state.</span></span>
<span class="line"><span style="color:#6272A4">// See Roman Kashitsyn's post for more info:</span></span>
<span class="line"><span style="color:#6272A4">// https://mmapped.blog/posts/01-effective-rust-canisters.html</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">thread_local!</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    static</span><span style="color:#BD93F9"> STATE</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> RefCell</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">State</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=</span><span style="color:#8BE9FD;font-style:italic"> RefCell</span><span style="color:#FF79C6">::</span><span style="color:#50FA7B">default</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// 🖖3️⃣ set the guard by its function's name</span></span>
<span class="line"><span style="color:#F8F8F2">#[query(guard </span><span style="color:#FF79C6">=</span><span style="color:#F1FA8C"> "caller_is_user"</span><span style="color:#F8F8F2">)]</span></span>
<span class="line"><span style="color:#FF79C6">fn</span><span style="color:#50FA7B"> greet</span><span style="color:#F8F8F2">(name</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> String</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">-></span><span style="color:#8BE9FD;font-style:italic"> String</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    format!</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">"Hello, {}!"</span><span style="color:#F8F8F2">, name)</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>And, that is basically it already 🥳.</p>
<p>The recipe:</p>
<ol>
<li>Create a guard function that returns an error if conditions are not met</li>
<li>Import the module</li>
<li>Annotate the functions that need to be protected</li>
<li>Having fun 😁</li>
</ol>
<hr />
<h2>Summary</h2>
<p>Not my longest post ever wrote but, I hope it will be useful for someone someday as it was for me to discover this small tricks.</p>
<p>To infinity and beyond<br />
David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Deploy Internet Identity locally]]></title>
          <description><![CDATA[How to deploy the blockchain authentication system for the Internet Computer locally.]]></description>
          <link>https://daviddalbusco.com/blog/deploy-internet-identity-locally/</link>
          <pubDate>Sun, 27 Nov 2022 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*E9xLy3n5oiiTp91mKI6TfA.png" alt="social-image.png" / loading="lazy"></p>
<p><em>Image source: <a href="https://dfinity.org/" rel="noopener noreferrer external">DFINITY foundation</a></em></p>
<p>So, you are looking to deploy <a href="https://identity.ic0.app/" rel="noopener noreferrer external">Internet Identity</a> (II) locally?</p>
<p>Here are my four easiest solution to do so!</p>
<hr />
<h2>Introduction</h2>
<p>To deploy II locally, we need to get its canister (Wasm module). We can either build the code ourself, using Docker and the <a href="https://github.com/dfinity/internet-identity/blob/main/scripts/docker-build" rel="noopener noreferrer external">docker-build</a> script provided by the II team, or download an existing <a href="https://github.com/dfinity/internet-identity/releases" rel="noopener noreferrer external">release</a>.</p>
<p>As I really dislike doing anything Devops related myself and, also because the goal of this tutorial is to make things happen fast, the following solutions are using the pre-build/released wasm (the above second option).</p>
<p>The first two options are applicable for any dapps, the last two are more useful if you start a new project.</p>
<hr />
<h2>1. Automatic Installation</h2>
<p>It is possible to set up II for a project with <code>dfx</code> &gt;= 0.12.0 using a <code>custom</code> canister.</p>
<p>So, just drop the following configuration in your project and that's already it.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#50FA7B">    "canisters"</span><span style="color:#8BE9FD">:</span><span style="color:#F1FA8C"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">        ...</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">          "internet_identity"</span><span style="color:#8BE9FD">:</span><span style="color:#F1FA8C"> {</span></span>
<span class="line"><span style="color:#50FA7B">            "type"</span><span style="color:#8BE9FD">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">custom</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">,</span></span>
<span class="line"><span style="color:#50FA7B">            "candid"</span><span style="color:#8BE9FD">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">https://github.com/dfinity/internet-identity/releases/latest/download/internet_identity.did</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">,</span></span>
<span class="line"><span style="color:#50FA7B">            "wasm"</span><span style="color:#8BE9FD">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">https://github.com/dfinity/internet-identity/releases/latest/download/internet_identity_dev.wasm.gz</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">,</span></span>
<span class="line"><span style="color:#50FA7B">            "shrink"</span><span style="color:#8BE9FD">:</span><span style="color:#BD93F9"> false</span><span style="color:#F1FA8C">,</span></span>
<span class="line"><span style="color:#50FA7B">            "remote"</span><span style="color:#8BE9FD">:</span><span style="color:#F1FA8C"> {</span></span>
<span class="line"><span style="color:#50FA7B">                "candid"</span><span style="color:#8BE9FD">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">internet_identity.did</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">,</span></span>
<span class="line"><span style="color:#50FA7B">                "id"</span><span style="color:#8BE9FD">:</span><span style="color:#F1FA8C"> {</span></span>
<span class="line"><span style="color:#50FA7B">                    "ic"</span><span style="color:#8BE9FD">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">rdmx6-jaaaa-aaaaa-aaadq-cai</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">                }</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"><span style="color:#F8F8F2">          }</span></span>
<span class="line"><span style="color:#F8F8F2">    },</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">    ...</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Using <code>dfx</code> &lt; v0.12.0, it's a bit more verbose - or hacky depends the point of view - but we can also leverage the <code>build</code> callback to download and install II automatically.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">canisters</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">: {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        ...</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E9F284">          "</span><span style="color:#F1FA8C">internet_identity</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">: {</span></span>
<span class="line"><span style="color:#E9F284">            "</span><span style="color:#F1FA8C">type</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">custom</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">            "</span><span style="color:#F1FA8C">candid</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">internet_identity.did</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">            "</span><span style="color:#F1FA8C">wasm</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">internet_identity.wasm.gz</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">            "</span><span style="color:#F1FA8C">build</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">bash -c 'test -f internet_identity.wasm.gz || curl -sSL https://github.com/dfinity/internet-identity/releases/latest/download/internet_identity_dev.wasm.gz -o internet_identity.wasm.gz; test -f internet_identity.did || curl -sSL https://raw.githubusercontent.com/dfinity/internet-identity/main/src/internet_identity/internet_identity.did -o internet_identity.did'</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">            "</span><span style="color:#F1FA8C">remote</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#E9F284">                "</span><span style="color:#F1FA8C">candid</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">internet_identity.did</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">                "</span><span style="color:#F1FA8C">id</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#E9F284">                    "</span><span style="color:#F1FA8C">ic</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">rdmx6-jaaaa-aaaaa-aaadq-cai</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">                }</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"><span style="color:#F8F8F2">          }</span></span>
<span class="line"><span style="color:#F8F8F2">    },</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    ...</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>As we only want to deploy II locally, it is worth to note that above config contains a <code>remote</code> option that points to II on mainnet. Thanks to it, the wasm won't be deployed when we deploy our canisters to mainnet.</p>
<p>You might also want to skip committing the <code>.did</code> and <code>.wasm.gz</code> files - that are going to be automatically downloaded - by omitting these in your <code>.gitignore</code>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">internet_identity.wasm.gz;</span></span>
<span class="line"><span style="color:#F8F8F2">internet_identity.did;</span></span></code></pre><hr />
<h2>2. Manual Installation</h2>
<p>If you prefer to not touch any configuration of your project, it is possible to deploy II locally manually as well. e.g. that's the strategy I personally use when I deploy <a href="https://papy.rs" rel="noopener noreferrer external">Papyrs</a>.</p>
<p>Anywhere on your computer, while <code>dfx start</code> has been started in your project, run following command lines and that's already it too.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">git</span><span style="color:#F1FA8C"> clone</span><span style="color:#F1FA8C"> https://github.com/dfinity/internet-identity</span></span>
<span class="line"><span style="color:#8BE9FD">cd</span><span style="color:#F1FA8C"> internet-identity/demos/using-dev-build</span></span>
<span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> ci</span></span>
<span class="line"><span style="color:#50FA7B">dfx</span><span style="color:#F1FA8C"> deploy</span><span style="color:#BD93F9"> --no-wallet</span><span style="color:#BD93F9"> --argument</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">(null)</span><span style="color:#E9F284">'</span></span></code></pre><hr />
<h2>3. New dapp</h2>
<p>If you start from scratch or looking to create a sample dapp, instead of initializing a new project with <code>dfx</code>, I recommend you to use the <code>npm</code> CLI (I have created). It prompts you interactively for options and setup everything for you including II.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> init</span><span style="color:#F1FA8C"> ic</span></span></code></pre><hr />
<h2>4. Game Changer</h2>
<p>I am working on a new secret side project that aims to reduce the gap for web2 developer on the IC early 2023 (🤯). Join my <a href="http://eepurl.com/iXHDy2" rel="noopener noreferrer external">newsletter</a> to keep hearing about it or reach me out on <a href="https://twitter.com/daviddalbusco" rel="noopener noreferrer external">Twitter</a> to get to know more!</p>
<p>To infinity and beyond<br />
David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Migrate canister smart contracts from Motoko to Rust]]></title>
          <description><![CDATA[I migrated 500+ canisters from Motoko to Rust on the Internet Computer. Here are the two major things I learned.]]></description>
          <link>https://daviddalbusco.com/blog/migrate-canister-smart-contracts-from-motoko-to-rust/</link>
          <pubDate>Thu, 10 Nov 2022 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://images.unsplash.com/photo-1464925885047-b49393632a77?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwzMDkyMzV8MHwxfHNlYXJjaHwxOHx8bWlncmF0ZXxlbnwwfHx8fDE2Njc3MjY0NDU&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080" alt="Whale tail" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@jeremybishop?utm_source=Papyrs&amp;utm_medium=referral" rel="noopener noreferrer external">Jeremy Bishop</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<hr />
<p>Last Friday (November 4, 2022) I upgraded the code of the 1'000+ canister smart contracts of <a href="https://papy.rs" rel="noopener noreferrer external">Papyrs</a> - an open-source, privacy-first, decentralized blogging platform. Among these, half of them have had their source code rewritten entirely from Motoko to Rust.</p>
<p>The <a href="https://internetcomputer.org/" rel="noopener noreferrer external">Internet Computer</a> - the general-purpose blockchain that hosts these canisters - <a href="https://internetcomputer.org/features/webassembly/" rel="noopener noreferrer external">uses the WebAssembly</a> VM to run its smart contracts. These can be written in various programming languages including the two languages mentioned above.</p>
<p>While on the paper and in practice switching the code base from one to the other works out like a charm, I had few challenges to solve first before being able to execute such a migration. Among these, two questions required a particular attention:</p>
<ol>
<li>Dynamically creating Rust canisters from a Motoko canister</li>
<li>Upgrading a rewritten smart contract while preserving its state</li>
</ol>
<p>Two topics I would like to share with you in this blog post.</p>
<hr />
<h2>1. Creating Rust canister on the fly</h2>
<p>On Papyrs there is no single smart contract that holds the data for all the users. Each of them get their own realm. Upon sign-in, each member of the community gets two canisters: one for the data (&quot;db&quot;) and another to serve the blogs on the web (a &quot;CDN storage&quot;).</p>
<p>As I was looking to migrate that last type of canisters, I had to find a way to create Rust based canister on the fly from my existing &quot;manager&quot; canister written in Motoko.</p>
<p><em>&gt; Interested to get to know how I dynamically create smart contracts on the fly in Motoko? Checkout this 👉 <a href="https://6zvwc-sqaaa-aaaal-aalma-cai.raw.ic0.app/d/dynamically-create-canister-smart-contracts-in-motoko" rel="noopener noreferrer external">blog post</a>.</em></p>
<p>I rapidly discovered that it was possible to solve such feature by actually changing few lines of my code. Indeed, importing a Rust canister within Motoko and within the same project with the help of a simple <code>import</code> just works out.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">// src/motoko_canister/main.mo</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> RustCanister </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">canister:rust_canister</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">actor Main {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    public shared ({ caller }) func hello() : async (Principal) {</span></span>
<span class="line"><span style="color:#F8F8F2">        let canisterId = await RustCanister.world(caller);</span></span>
<span class="line"><span style="color:#F8F8F2">        return canisterId;</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// src/rust_canister/src/lib.rs</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">use candid::Principal;</span></span>
<span class="line"><span style="color:#F8F8F2">use ic_cdk_macros::{update};</span></span>
<span class="line"><span style="color:#F8F8F2">use ic_cdk::{api};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">#[update]</span></span>
<span class="line"><span style="color:#F8F8F2">async fn create_bucket(_user_id: Principal) -> Principal {</span></span>
<span class="line"><span style="color:#F8F8F2">    let caller = api::caller();</span></span>
<span class="line"><span style="color:#F8F8F2">    caller</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>However, this was not exactly my use case - i.e. I was not looking to call one given particular canister but rather to create new smart contracts on the fly.</p>
<p>After following various paths, I finally chose the one that consists of implementing a generic solution. I implemented a function that creates new canister and install code from wasm bytes. That way, it does not matter if the code of the canisters has been developed in Rust or any other language.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> Cycles </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">mo:base/ExperimentalCycles</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">import Principal </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">mo:base/Principal</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">import Blob </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">mo:base/Blob</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// types for the IC. e.g. available in https://github.com/papyrs/ic</span></span>
<span class="line"><span style="color:#F8F8F2">import IC </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">./ic.types</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">actor Main {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    private let ic : IC.Self = actor </span><span style="color:#F1FA8C">"aaaaa-aa"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    private stable var storageWasm: [Nat8] = [];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    public shared ({ caller }) func init() : async (Principal) {</span></span>
<span class="line"><span style="color:#6272A4">        // Indicates the cycles to be transferred in the next call</span></span>
<span class="line"><span style="color:#F8F8F2">        Cycles.add(1_000_000_000_000);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">        // Create a new canister</span></span>
<span class="line"><span style="color:#F8F8F2">        let { canister_id } = await ic.create_canister({ settings = null });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">        // Set the controllers of the new canister</span></span>
<span class="line"><span style="color:#6272A4">        // In this case this canister, the caller and the canister itself</span></span>
<span class="line"><span style="color:#F8F8F2">        let self : Principal = Principal.fromActor(Main);</span></span>
<span class="line"><span style="color:#F8F8F2">        let controllers : ?[Principal] = ?[canister_id, caller, self];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">        // Update the settings to apply the controllers</span></span>
<span class="line"><span style="color:#F8F8F2">        await ic.update_settings(({</span></span>
<span class="line"><span style="color:#F8F8F2">            canister_id;</span></span>
<span class="line"><span style="color:#F8F8F2">            settings = {</span></span>
<span class="line"><span style="color:#F8F8F2">                controllers = controllers;</span></span>
<span class="line"><span style="color:#F8F8F2">                freezing_threshold = null;</span></span>
<span class="line"><span style="color:#F8F8F2">                memory_allocation = null;</span></span>
<span class="line"><span style="color:#F8F8F2">                compute_allocation = null;</span></span>
<span class="line"><span style="color:#F8F8F2">            };</span></span>
<span class="line"><span style="color:#F8F8F2">        }));</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">        // Example. to_candid can be used to encode initial args</span></span>
<span class="line"><span style="color:#F8F8F2">        let arg: Blob = to_candid(caller);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">        // Finally install the generic wasm code</span></span>
<span class="line"><span style="color:#F8F8F2">        await ic.install_code({</span></span>
<span class="line"><span style="color:#F8F8F2">            arg;</span></span>
<span class="line"><span style="color:#F8F8F2">            wasm_module = Blob.fromArray(storageWasm);</span></span>
<span class="line"><span style="color:#F8F8F2">            mode = #install;</span></span>
<span class="line"><span style="color:#F8F8F2">            canister_id;</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        return canister_id;</span></span>
<span class="line"><span style="color:#F8F8F2">  };</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>What's happening here?</p>
<ol>
<li>The function starts by indicating how much cycles will be transferred in the next call to create a new canister.</li>
<li>The creation happens and the IC provides the new canister ID.</li>
<li>In my particular use case, I want the user and the &quot;manager&quot; (until hopefully someday I defer Papyrs to a Sns) to be set as controllers of the newly created canister. That is why the function update these settings. Note that I am not sure if this step can be spared by providing the controllers when creating the canister in previous step 2. As far as I remember it did not worked out once so I always proceed that way.</li>
<li>It encodes the arguments that are needed to initialize the wasm code. If there is no initialization parameters this can be spared.</li>
<li>It finally installs the wasm code - i.e. the Rust code I have written and compiled.</li>
</ol>
<p>At this point, you might ask yourself where does the wasm code comes from? I will explain this promptly.</p>
<p>At first, I had that idea to load the bytes at build time when the Motoko code was compiled. This unfortunately did not succeed because of memory limitation. After discussing the issue on the <a href="https://forum.dfinity.org/t/read-local-file-at-build-time-with-motoko/15945" rel="noopener noreferrer external">forum</a>, <a href="https://twitter.com/afat" rel="noopener noreferrer external">Austin Fatheree</a> shared the idea to upload the wasm code in memory once the &quot;manager&quot; canister has been deployed. This was the solution I selected and implemented.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">actor Main {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    private stable </span><span style="color:#FF79C6">var</span><span style="color:#F8F8F2"> storageWasm</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#8BE9FD;font-style:italic">Nat8</span><span style="color:#F8F8F2">] </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> [];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    public </span><span style="color:#50FA7B">shared</span><span style="color:#F8F8F2"> ({ caller }) func </span><span style="color:#50FA7B">storateResetWasm</span><span style="color:#F8F8F2">(): </span><span style="color:#50FA7B">async</span><span style="color:#F8F8F2"> () {</span></span>
<span class="line"><span style="color:#6272A4">      // Reject invalid caller</span></span>
<span class="line"><span style="color:#F8F8F2">      storageWasm :</span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> [];</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    public </span><span style="color:#50FA7B">shared</span><span style="color:#F8F8F2"> ({ caller }) func </span><span style="color:#50FA7B">storageLoadWasm</span><span style="color:#F8F8F2">(blob: [Nat8]): </span><span style="color:#50FA7B">async</span><span style="color:#F8F8F2"> ({total</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> Nat; chunks: Nat;}) {</span></span>
<span class="line"><span style="color:#6272A4">        // Reject invalid caller</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">        // Note: Array.append is deprecated but buffer.append needs dfx v12</span></span>
<span class="line"><span style="color:#6272A4">        // Issue: https://forum.dfinity.org/t/array-to-buffer-in-motoko/15880/15</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        storageWasm :</span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> Array.</span><span style="color:#50FA7B">append</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Nat8</span><span style="color:#F8F8F2">>(storageWasm, blob);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">        // Return total wasm sizes</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">            total </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> storageWasm.</span><span style="color:#50FA7B">size</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">            chunks </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> blob.</span><span style="color:#50FA7B">size</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>I added two functions to the canister that creates the other canisters. One to reset the state and another that appends the wasm code chunks to the local state.</p>
<p>⚠️ These functions need some proper assertions not implemented in above snippet. You most probably do not want attackers to be able to overwrite and replace the state with some malicious wasm code.</p>
<p>Finally, I created a NodeJS script that reads the wasm code and calls above endpoints to load the wasm code I have developed in the memory of the &quot;manager&quot;.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { readFile } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">fs/promises</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { canisterId, managerActor } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./manager.actor.mjs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> loadWasm</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> buffer </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> readFile</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#F1FA8C">        `</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">process.</span><span style="color:#50FA7B">cwd</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">/.dfx/local/canisters/my_rust_canister/my_rust_canister.wasm`</span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> [</span><span style="color:#FF79C6">...</span><span style="color:#FF79C6;font-weight:bold">new</span><span style="color:#50FA7B"> Uint8Array</span><span style="color:#F8F8F2">(buffer)];</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> resetWasm</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#F8F8F2"> managerActor.</span><span style="color:#50FA7B">storateResetWasm</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> installWasm</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">wasmModule</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`Installing wasm code in: </span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">canisterId</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> chunkSize </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> 700000</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#50FA7B"> upload</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">chunks</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> result </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> managerActor.</span><span style="color:#50FA7B">storageLoadWasm</span><span style="color:#F8F8F2">(chunks);</span></span>
<span class="line"><span style="color:#F8F8F2">        console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Chunks:</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, result);</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    for</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">let</span><span style="color:#F8F8F2"> start </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">; start </span><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2"> wasmModule.length; start </span><span style="color:#FF79C6">+=</span><span style="color:#F8F8F2"> chunkSize) {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> chunks </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> wasmModule.</span><span style="color:#50FA7B">slice</span><span style="color:#F8F8F2">(start, start </span><span style="color:#FF79C6">+</span><span style="color:#F8F8F2"> chunkSize);</span></span>
<span class="line"><span style="color:#FF79C6">        await</span><span style="color:#50FA7B"> upload</span><span style="color:#F8F8F2">(chunks);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`Done: </span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">canisterId</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> wasmModule </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> loadWasm</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // Install wasm in manager</span></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#50FA7B"> resetWasm</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#50FA7B"> installWasm</span><span style="color:#F8F8F2">(wasmModule);</span></span>
<span class="line"><span style="color:#F8F8F2">})();</span></span></code></pre><hr />
<h2>2. Upgrading and preserving state</h2>
<p>While previous chapter was probably more related to the way Papyrs behaves, being able to migrate a Motoko canister to Rust while preserving its state on mainnet definitely could have been a show stopper if it would not have been supported. Of course, spoiler alert 😜, it is and the migration went absolutely smoothly.</p>
<p>One key feature of the Internet Computer is its ability to persist canister smart contract state using WebAssembly memory and globals.</p>
<p>That is why a canister can be upgraded regardless of the programming language.</p>
<p>Being said, fine it is, easy maybe a bit less. Particularly for a fully Rust newbie as myself 😅.</p>
<p>Given a following canister, I reasonably assumed that the stable memory had to be decoded in the new one, in the one written in Rust. However, I had no idea how 🤷‍♂️.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> Time </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">mo:base/Time</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">import Blob </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">mo:base/Blob</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">import Text </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">mo:base/Text</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">actor Demo {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    private stable var test: Nat = 666;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    private </span><span style="color:#FF79C6">type</span><span style="color:#F8F8F2"> Asset = {</span></span>
<span class="line"><span style="color:#F8F8F2">        key : Text;</span></span>
<span class="line"><span style="color:#F8F8F2">        modified : Int;</span></span>
<span class="line"><span style="color:#F8F8F2">        contentChunks: [[Nat8]];</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    private stable var entries: [(Text, Asset)] = [];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    system func preupgrade() {</span></span>
<span class="line"><span style="color:#F8F8F2">        entries := [(</span><span style="color:#F1FA8C">"yolo"</span><span style="color:#F8F8F2">, {</span></span>
<span class="line"><span style="color:#F8F8F2">            key = </span><span style="color:#F1FA8C">"hello"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">            modified = Time.now();</span></span>
<span class="line"><span style="color:#F8F8F2">            contentChunks = [Blob.toArray(Text.encodeUtf8(</span><span style="color:#F1FA8C">"world"</span><span style="color:#F8F8F2">))];</span></span>
<span class="line"><span style="color:#F8F8F2">        })]</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    system func postupgrade() {</span></span>
<span class="line"><span style="color:#6272A4">        // Postupgrade will happens in Rust.</span></span>
<span class="line"><span style="color:#6272A4">        // Memory has to be decoded there.</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Fortunately the amazing IC community shined once again and thanks to the help of <a href="https://forum.dfinity.org/u/frederikrothenberger" rel="noopener noreferrer external">Frederik Rothenberger</a> and particularly to <a href="https://forum.dfinity.org/u/senior.joinu" rel="noopener noreferrer external">Alexander Vtyurin</a> code snippet, the biggest issue I had could be resolved on the <a href="https://forum.dfinity.org/t/upgrade-canister-from-motoko-to-rust-with-stable-memory/" rel="noopener noreferrer external">forum</a>!</p>
<p>In Rust, the stable memory that has been populated before the upgrade can be read in a post upgrade hook with the help of the <a href="https://docs.rs/ic-cdk/latest/ic_cdk/api/stable/index.html" rel="noopener noreferrer external">stable API</a> and decoded using candid <a href="https://docs.rs/candid/latest/candid/utils/fn.decode_args.html" rel="noopener noreferrer external">decode_args</a>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">mod</span><span style="color:#F8F8F2"> types;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">use</span><span style="color:#F8F8F2"> ic_cdk</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">{api</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">{ stable</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2"> { stable_read } }};</span></span>
<span class="line"><span style="color:#FF79C6">use</span><span style="color:#F8F8F2"> candid</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">{decode_args};</span></span>
<span class="line"><span style="color:#FF79C6">use</span><span style="color:#F8F8F2"> ic_cdk_macros</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">{post_upgrade};</span></span>
<span class="line"><span style="color:#FF79C6">use</span><span style="color:#F8F8F2"> std</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">cell</span><span style="color:#FF79C6">::</span><span style="color:#8BE9FD;font-style:italic">RefCell</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// UpgradeState is a custom type</span></span>
<span class="line"><span style="color:#FF79C6">use</span><span style="color:#FF79C6"> crate::</span><span style="color:#F8F8F2">types</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">{demo</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">{</span><span style="color:#8BE9FD;font-style:italic">State</span><span style="color:#F8F8F2">}};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">thread_local!</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    static</span><span style="color:#BD93F9"> STATE</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> RefCell</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">State</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=</span><span style="color:#8BE9FD;font-style:italic"> RefCell</span><span style="color:#FF79C6">::</span><span style="color:#50FA7B">default</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">#[post_upgrade]</span></span>
<span class="line"><span style="color:#FF79C6">fn</span><span style="color:#50FA7B"> post_upgrade</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#6272A4">    // By senior.joinu - not all heroes wear capes</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // BEGIN: read the stable memory in a buffer</span></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#FF79C6"> mut</span><span style="color:#F8F8F2"> stable_length_buf </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> [</span><span style="color:#BD93F9">0</span><span style="color:#8BE9FD;font-style:italic">u8</span><span style="color:#F8F8F2">; std</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">mem</span><span style="color:#FF79C6">::</span><span style="color:#50FA7B">size_of</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">u32</span><span style="color:#F8F8F2">>()];</span></span>
<span class="line"><span style="color:#50FA7B">    stable_read</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">, </span><span style="color:#FF79C6">&#x26;mut</span><span style="color:#F8F8F2"> stable_length_buf);</span></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> stable_length </span><span style="color:#FF79C6">=</span><span style="color:#8BE9FD;font-style:italic"> u32</span><span style="color:#FF79C6">::</span><span style="color:#50FA7B">from_le_bytes</span><span style="color:#F8F8F2">(stable_length_buf);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#FF79C6"> mut</span><span style="color:#F8F8F2"> buf </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> vec!</span><span style="color:#F8F8F2">[</span><span style="color:#BD93F9">0</span><span style="color:#8BE9FD;font-style:italic">u8</span><span style="color:#F8F8F2">; stable_length </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> usize</span><span style="color:#F8F8F2">];</span></span>
<span class="line"><span style="color:#50FA7B">    stable_read</span><span style="color:#F8F8F2">(std</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">mem</span><span style="color:#FF79C6">::</span><span style="color:#50FA7B">size_of</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">u32</span><span style="color:#F8F8F2">>() </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> u32</span><span style="color:#F8F8F2">, </span><span style="color:#FF79C6">&#x26;mut</span><span style="color:#F8F8F2"> buf);</span></span>
<span class="line"><span style="color:#6272A4">    // END: read</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // Decode the memory buffer</span></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> (state,)</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> (</span><span style="color:#8BE9FD;font-style:italic">State</span><span style="color:#F8F8F2">,) </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> decode_args</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">&#x26;</span><span style="color:#F8F8F2">buf)</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">unwrap</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // e.g. populate state</span></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> new_state</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> State</span><span style="color:#FF79C6"> =</span><span style="color:#8BE9FD;font-style:italic"> State</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        test</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> state</span><span style="color:#FF79C6">.</span><span style="color:#F8F8F2">test,</span></span>
<span class="line"><span style="color:#F8F8F2">        entries</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> state</span><span style="color:#FF79C6">.</span><span style="color:#F8F8F2">entries</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#BD93F9">    STATE</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">with</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">|</span><span style="color:#F8F8F2">state</span><span style="color:#FF79C6">|</span><span style="color:#FF79C6"> *</span><span style="color:#F8F8F2">state</span><span style="color:#FF79C6">.</span><span style="color:#50FA7B">borrow_mut</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> new_state);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>While this was a major milestone, the challenge was not over yet. The memory still had to be &quot;re-structured&quot;.</p>
<p>For this specific task I did not find any silver bullet. The types had to be manually converted - i.e. I went through my Motoko code and I rewrote all the types from scratch (I know, I have got weird hobbies 😅).</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">pub</span><span style="color:#FF79C6"> mod</span><span style="color:#F8F8F2"> demo {</span></span>
<span class="line"><span style="color:#FF79C6">    use</span><span style="color:#F8F8F2"> candid</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">{</span><span style="color:#8BE9FD;font-style:italic">CandidType</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">Int</span><span style="color:#F8F8F2">};</span></span>
<span class="line"><span style="color:#FF79C6">    use</span><span style="color:#F8F8F2"> serde</span><span style="color:#FF79C6">::</span><span style="color:#F8F8F2">{</span><span style="color:#8BE9FD;font-style:italic">Serialize</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">Deserialize</span><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    #[derive(</span><span style="color:#8BE9FD;font-style:italic">CandidType</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">Deserialize</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">Serialize</span><span style="color:#F8F8F2">)]</span></span>
<span class="line"><span style="color:#FF79C6">    pub</span><span style="color:#FF79C6"> struct</span><span style="color:#8BE9FD;font-style:italic"> Asset</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        pub</span><span style="color:#F8F8F2"> key</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> String</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#FF79C6">        pub</span><span style="color:#F8F8F2"> modified</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Int</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#FF79C6">        pub</span><span style="color:#F8F8F2"> contentChunks</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Vec</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">Vec</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">u8</span><span style="color:#F8F8F2">>>,</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    #[derive(</span><span style="color:#8BE9FD;font-style:italic">Default</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">CandidType</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">Deserialize</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">Serialize</span><span style="color:#F8F8F2">)]</span></span>
<span class="line"><span style="color:#FF79C6">    pub</span><span style="color:#FF79C6"> struct</span><span style="color:#8BE9FD;font-style:italic"> State</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        pub</span><span style="color:#F8F8F2"> test</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Option</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">u128</span><span style="color:#F8F8F2">>,</span></span>
<span class="line"><span style="color:#FF79C6">        pub</span><span style="color:#F8F8F2"> entries</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Option</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">Vec</span><span style="color:#F8F8F2">&#x3C;(</span><span style="color:#8BE9FD;font-style:italic">String</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">Asset</span><span style="color:#F8F8F2">)>>,</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Above snippet are the corresponding types for the preceding Motoko code and here are the most important takeover of such a conversion:</p>
<ul>
<li>variable names have to match. If a stable variable is named <code>test</code> in Motoko, it has to be named <code>test</code> in Rust.</li>
<li>all root types that are decoded from the stable memory become optional regardless if they were declared mandatory or not in previous code.</li>
<li>not declaring these types as optional leads to a decoding error that will be thrown in the local IC started with dfx.</li>
<li>any type at any levels of the conversion that does not match the original type will lead to a silent error (⚠️) and will have for effect to decode to none. e.g. in above example, if I would had declared <code>modified</code> as a <code>u128</code> instead of a the correct <code>candid::int</code> - which relates to the Motoko <code>Time</code> - the all <code>entries</code> would just have been decoded to none. No error, no stracktrace, just none and an ocean of tears.</li>
</ul>
<p>Once the memory read and re-structured, the upgrade was almost achieved. There was no other big blockers. I could persist the state and all data were still there.</p>
<hr />
<h2>Sample repo</h2>
<p>To validate the hypothesis of such a migration, I developed both chapters in two distinctive sample repo. Their code is not as clean as the final implementation but if they can be useful here are these:</p>
<ul>
<li>Motoko Rust interop validation 👉 <a href="https://github.com/peterpeterparker/motoko_rust_interop" rel="noopener noreferrer external">https://github.com/peterpeterparker/motoko_rust_interop</a></li>
<li>Motoko to Rust migration 👉 <a href="https://github.com/peterpeterparker/motoko_to_rust_migration" rel="noopener noreferrer external">https://github.com/peterpeterparker/motoko_to_rust_migration</a></li>
</ul>
<p>My migration being over, the related code I executed on mainnet has been archived but, you can still find it in the history of the backend and providers of Papyrs 👉 <a href="https://github.com/papyrs/ic" rel="noopener noreferrer external">https://github.com/papyrs/ic</a></p>
<hr />
<h2>Summary</h2>
<p>I migrated 500+ canisters from Motoko to Rust on the Internet Computer.</p>
<p>Seriously, I am not gonna lie and because I did not trust myself on this, I almost still cannot believe that it worked out on mainnet without a single issue 🤯.</p>
<p>It is probably due to the fact that I executed the all migration process at least 50th times from scratch locally and few times on mainnet as well.</p>
<p>Therefore, if I can share one last advice if you intent to perform such a migration too: test, test &amp; test before executing!</p>
<p>To infinity and beyond<br />
David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Few things to know before launching a SvelteKit app in prod]]></title>
          <description><![CDATA[Content security policy, build reproducibility and polyfill. Few tips to know before going live with SvelteKit.]]></description>
          <link>https://daviddalbusco.com/blog/few-things-to-know-before-launching-a-sveltekit-app-in-prod/</link>
          <pubDate>Wed, 19 Oct 2022 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://images.unsplash.com/photo-1636819488524-1f019c4e1c44?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwzMDkyMzV8MHwxfHNlYXJjaHw5fHxsYXVuY2h8ZW58MHx8fHwxNjY2MDE4Njg1&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=1080" alt="3D Rendering Rocket Space Launching Illustration" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@kolamdigital?utm_source=Papyrs&amp;utm_medium=referral" rel="noopener noreferrer external">Andy Hermawan</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<hr />
<p>Last week new version of <a href="https://internetcomputer.org/nns" rel="noopener noreferrer external">NNS-dapp</a> (the dapp of NNS, one of the world's largest DAOs that governs the <a href="https://internetcomputer.org/" rel="noopener noreferrer external">Internet Computer</a>) introduced a new feature named &quot;Stake Maturity&quot;, a light design update of its modals and, a change in its build system.</p>
<p>Indeed, while the frontend application used to be packaged with the help of the sole <a href="https://rollupjs.org/" rel="noopener noreferrer external">Rollup</a> bundler, it was migrated to <a href="https://kit.svelte.dev/" rel="noopener noreferrer external">SvelteKit</a>* which uses both <a href="https://vitejs.dev/" rel="noopener noreferrer external">Vite</a>, <a href="https://esbuild.github.io/" rel="noopener noreferrer external">esbuild</a> and Rollup.</p>
<p>Here are the three things I learned along the way. I hope they will be helpful to you so that you can deploy your applications safely in production too.</p>
<p><em>* without any changes regarding routing, yet</em></p>
<hr />
<h2>1. CSP breaks app in Firefox</h2>
<p>Content Security Policy (CSP) is an added layer of security that helps to detect and mitigate certain types of attacks, including Cross-Site Scripting (XSS) and data injection attacks (source <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP" rel="noopener noreferrer external">MDN</a>).</p>
<p>As we care about security, of course we have implemented such types of rules. Notably the policies <code>script-src</code> that whitelists the script tags of the <code>index.html</code> page - using their <code>sha256</code> script hashes - and the <code>'strict-dynamic'</code> which is used to load all the chunks of code that are needed to run the application in the browser.</p>
<p>While this worked out with previous bundler, we were actually surprised to discover that SvelteKit (Oct. 2022) does not really support such combination of policies (see issue <a href="https://github.com/sveltejs/kit/issues/3558" rel="noopener noreferrer external">#3558</a>). It works in Chrome and Safari but, breaks in Firefox and with &quot;break&quot; I mean the all app won't be rendered at all.</p>
<p>To overcome the issue, we found the following workaround: adding a post-build script that extracts into a separate JS file the code that is injected in the HTML page by SvelteKit and, inject our own script loader instead 🤪.</p>
<p>This can be achieved as following:</p>
<p>1. add an empty <code>main.js</code> to the <code>static</code> folder (useful to avoid issue while developing locally).</p>
<p>2. add a script loader in the <code>&lt;head /&gt;</code> of the root html page - i.e. in the <code>src/app.html</code> page.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> loader </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> document.</span><span style="color:#50FA7B">createElement</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">script</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    loader.type </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">module</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    loader.src </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./main.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    document.head.</span><span style="color:#50FA7B">appendChild</span><span style="color:#F8F8F2">(loader);</span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span></code></pre><p>3. create a post-build script - e.g. <code>./scripts/build.csp.mjs</code>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">#!/usr/bin/env node</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { readFileSync, writeFileSync } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">fs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { join } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">path</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> publicIndexHTML </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> join</span><span style="color:#F8F8F2">(process.</span><span style="color:#50FA7B">cwd</span><span style="color:#F8F8F2">(), </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">public</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">index.html</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> buildCsp</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> indexHTMLWithoutStartScript </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> extractStartScript</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#50FA7B">    writeFileSync</span><span style="color:#F8F8F2">(publicIndexHTML, indexHTMLWithoutStartScript);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">/**</span></span>
<span class="line"><span style="color:#6272A4"> * Using a CSP with 'strict-dynamic' with SvelteKit breaks in Firefox.</span></span>
<span class="line"><span style="color:#6272A4"> * Issue: https://github.com/sveltejs/kit/issues/3558</span></span>
<span class="line"><span style="color:#6272A4"> *</span></span>
<span class="line"><span style="color:#6272A4"> * As workaround:</span></span>
<span class="line"><span style="color:#6272A4"> * 1. we extract the start script that is injected by SvelteKit in index.html into a separate main.js</span></span>
<span class="line"><span style="color:#6272A4"> * 2. we remove the script content from index.html but, let the script tag as anchor</span></span>
<span class="line"><span style="color:#6272A4"> * 3. we use our custom script loader to load the main.js script</span></span>
<span class="line"><span style="color:#6272A4"> */</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> extractStartScript</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> indexHtml </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> readFileSync</span><span style="color:#F8F8F2">(publicIndexHTML, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">utf-8</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> svelteKitStartScript </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#FF5555">        /</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">&#x3C;script type=\"module\" data-sveltekit-hydrate</span><span style="color:#8BE9FD">[</span><span style="color:#BD93F9">\s\S</span><span style="color:#8BE9FD">]</span><span style="color:#FF79C6">*?</span><span style="color:#F1FA8C">></span><span style="color:#FFB86C">)(</span><span style="color:#8BE9FD">[</span><span style="color:#BD93F9">\s\S</span><span style="color:#8BE9FD">]</span><span style="color:#FF79C6">*?</span><span style="color:#FFB86C">)(</span><span style="color:#F1FA8C">&#x3C;\/script></span><span style="color:#FFB86C">)</span><span style="color:#FF5555">/</span><span style="color:#FF79C6">gm</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // 1. extract SvelteKit start script to a separate main.js file</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> [_script, _scriptStartTag, content, _scriptEndTag] </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> svelteKitStartScript.</span><span style="color:#50FA7B">exec</span><span style="color:#F8F8F2">(indexHtml);</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> inlineScript </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> content.</span><span style="color:#50FA7B">replace</span><span style="color:#F8F8F2">(</span><span style="color:#FF5555">/</span><span style="color:#FF79C6">^</span><span style="color:#BD93F9">\s</span><span style="color:#FF79C6">*</span><span style="color:#FF5555">/</span><span style="color:#FF79C6">gm</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">""</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    writeFileSync</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">join</span><span style="color:#F8F8F2">(process.</span><span style="color:#50FA7B">cwd</span><span style="color:#F8F8F2">(), </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">public</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">main.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">), inlineScript, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">utf-8</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // 2. replace SvelteKit script tag content with empty</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> indexHtml.</span><span style="color:#50FA7B">replace</span><span style="color:#F8F8F2">(svelteKitStartScript, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">$1$3</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">buildCsp</span><span style="color:#F8F8F2">();</span></span></code></pre><p>4. chain the script in <code>package.json</code>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">scripts</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">build:csp</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">node scripts/build.csp.mjs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">build</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">vite build &#x26;&#x26; npm run build:csp</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h2>2. Build reproducibility</h2>
<p>Reproducible builds is a process of compiling software which ensures the resulting binary code can be reproduced (source <a href="https://en.wikipedia.org/wiki/Reproducible_builds" rel="noopener noreferrer external">wikipedia</a>). We care about deterministic compilation because we want to allow verification that no vulnerabilities or backdoors have been introduced during the compilation process.</p>
<p>This had always worked out like a charm. However, after the migration, we were not able to compute the same sha for the bundled wasm on multiple computers anymore.</p>
<p>After some debugging we found the two root causes of the issue.</p>
<p>1. if no particular <a href="https://kit.svelte.dev/docs/configuration#version" rel="noopener noreferrer external">version</a> is provided to SvelteKit, it will instead generates a timestamp to identify the current app version - i.e. if no version is provided, SvelteKit injects a timestamp in the JS code that gets bundled. Each build, each time a new timestamp.</p>
<p>To solve this, we read the version number in <code>package.json</code> and we provided it to the kit in the <code>svelte.config.js</code>. In that way, the version become static for each build as long as we do not bump the semantic numbers.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">import adapter from </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">@sveltejs/adapter-static</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">import autoprefixer from </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">autoprefixer</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">import { </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">readFileSync</span><span style="color:#F8F8F2"> } from </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">fs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">import preprocess from </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">svelte-preprocess</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">import { </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">fileURLToPath</span><span style="color:#F8F8F2"> } from </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">url</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">const file = fileURLToPath(new URL(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">package.json</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, import.meta.url));</span></span>
<span class="line"><span style="color:#F8F8F2">const json = readFileSync(file, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">utf8</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">const { </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">version</span><span style="color:#F8F8F2"> } = JSON.parse(json);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">const config = {</span></span>
<span class="line"><span>  </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">preprocess</span><span style="color:#FF79C6">:</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">preprocess(</span><span style="color:#F8F8F2">{</span></span>
<span class="line"><span>    </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">postcss</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span>      </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">plugins</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">autoprefixer</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">    },</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">)</span><span style="color:#F8F8F2">,</span></span>
<span class="line"></span>
<span class="line"><span>  </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">kit</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span>    </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">adapter</span><span style="color:#FF79C6">:</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">adapter(</span><span style="color:#F8F8F2">{</span></span>
<span class="line"><span>      </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">pages</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">public</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span>      </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">assets</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">public</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span>      </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">fallback</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">index.html</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span>      </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">precompress</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">)</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span>    </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">serviceWorker</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span>      </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">register</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    },</span></span>
<span class="line"><span>    </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">version</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span>      </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">name</span><span style="color:#FF79C6">:</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">version</span><span style="color:#F8F8F2">, </span><span style="color:#6272A4">// &#x3C;---- here provide version</span></span>
<span class="line"><span style="color:#F8F8F2">    },</span></span>
<span class="line"><span>    </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">trailingSlash</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">always</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  },</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">export default config;</span></span></code></pre><p>2. SvelteKit - or Vite - adds a <code>public/vite-manifest.json</code> file that contains the list of all generated immutable assets of the application. This file is unfortunately currently not sorted. Therefore as a quick fix, we added a bash script to do so.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">#!/usr/bin/env bash</span></span>
<span class="line"><span style="color:#8BE9FD">set</span><span style="color:#BD93F9"> -euxo</span><span style="color:#F1FA8C"> pipefail</span></span>
<span class="line"><span style="color:#8BE9FD">cd</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">$(</span><span style="color:#50FA7B">dirname</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">$(</span><span style="color:#50FA7B">realpath</span><span style="color:#E9F284"> "</span><span style="color:#FFB86C;font-style:italic">$0</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">)</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">)/..</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#6272A4"># shellcheck disable=SC2094 # This reads the entire file into memory and then writes it out, so is correct.</span></span>
<span class="line"><span style="color:#50FA7B">cat</span><span style="color:#FF79C6"> &#x3C;&#x3C;&#x3C;</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">$(</span><span style="color:#50FA7B">jq</span><span style="color:#BD93F9"> --sort-keys</span><span style="color:#F1FA8C"> . public/vite-manifest.json)</span><span style="color:#E9F284">"</span><span style="color:#FF79C6"> ></span><span style="color:#F1FA8C">public/vite-manifest.json</span></span></code></pre><p>Bash script which we chained in <code>package.json</code> as well.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">scripts</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">build:csp</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">node scripts/build.csp.mjs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">build</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">vite build &#x26;&#x26; npm run build:csp &#x26;&#x26; ./scripts/make-reproducible</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h2>3. Polyfill Buffer</h2>
<p>I had always used Chovy's <a href="https://stackoverflow.com/a/72220289/5404186" rel="noopener noreferrer external">SO solution</a> to polyfill the Buffer API for frontend dapps on the IC but, it did not worked out entirely anymore. While redefining <code>global</code> as <code>globalThis</code> in <code>vite.config.js</code> still did the job, no polyfill for the &quot;Buffer&quot; was applied.</p>
<p>That is why we added a &quot;manual&quot; polyfill in the root <code>+layout.ts</code> after having installed (<code>npm i buffer</code>) the <a href="https://www.npmjs.com/package/buffer" rel="noopener noreferrer external">buffer</a> module dependency for the browser.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Buffer } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">buffer</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">globalThis.Buffer </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> Buffer;</span></span></code></pre><p>However, we discovered that this works as intended locally, with a development or production build, but, might become an issue in production because there is no guarantee that the <code>+layout.js</code> file will be fetched faster than the pages that use it.</p>
<p>That is why in addition to the above add-ons, it is worth to inject the polyfied Buffer within the production JS code that is bundled. This can be done with the help of a Rollup plugin (<code>npm i @rollup/plugin-inject -D</code>).</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> inject </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@rollup/plugin-inject</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { sveltekit } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@sveltejs/kit/vite</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#FF79C6"> type</span><span style="color:#F8F8F2"> { UserConfig } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">vite</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> config</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> UserConfig</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  plugins</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#50FA7B">sveltekit</span><span style="color:#F8F8F2">()],</span></span>
<span class="line"><span style="color:#F8F8F2">  build</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    target</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">es2020</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    rollupOptions</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#6272A4">      // Polyfill Buffer for production build.</span></span>
<span class="line"><span style="color:#6272A4">      // The hardware wallet needs Buffer.</span></span>
<span class="line"><span style="color:#F8F8F2">      plugins</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#50FA7B">        inject</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">          include</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">node_modules/@ledgerhq/**</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">          modules</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { Buffer</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">buffer</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Buffer</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">] },</span></span>
<span class="line"><span style="color:#F8F8F2">        }),</span></span>
<span class="line"><span style="color:#F8F8F2">      ],</span></span>
<span class="line"><span style="color:#F8F8F2">    },</span></span>
<span class="line"><span style="color:#F8F8F2">  },</span></span>
<span class="line"><span style="color:#F8F8F2">  optimizeDeps</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    esbuildOptions</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#6272A4">      // Node.js global to browser globalThis</span></span>
<span class="line"><span style="color:#F8F8F2">      define</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        global</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">globalThis</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">      },</span></span>
<span class="line"><span style="color:#F8F8F2">    },</span></span>
<span class="line"><span style="color:#F8F8F2">  },</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> config;</span></span></code></pre><p>Notes:</p>
<ul>
<li>we need above polyfill for hardware wallet related features. That is why we scope it to the <code>ledgerhq</code> library when we use the Rollup plugin.</li>
<li>the solution is not yet optimal because we apply the polyfill twice - i.e. we actually load too much JavaScript code in the production build. Never too sure I agree but, still, this can be improved.</li>
<li>web worker code does not get polyfied with above solution. If you need to do so, you probably will need to investigate further.</li>
</ul>
<hr />
<h2>Conclusion</h2>
<p>It's all fun and games until you discover issues which do not exist when you develop locally 😁. I am glad we solved all these hickups and were able to migrate. Using ViteJS ease the developer experience and porting the dapp to SvelteKit opens new possibilities, notably some ideas we have about the routing but, I will probably tell more about it in another blog post 😉.</p>
<p>To infinity and beyond<br />
David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Poll canister on the Internet Computer]]></title>
          <description><![CDATA[Repeatedly calls a function on the IC with web workers without decreasing your dapp frontend performance.]]></description>
          <link>https://daviddalbusco.com/blog/poll-canister-on-the-internet-computer/</link>
          <pubDate>Mon, 10 Oct 2022 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://images.unsplash.com/photo-1524678714210-9917a6c619c2?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwzMDkyMzV8MHwxfHNlYXJjaHwxMHx8dGltZXJ8ZW58MHx8fHwxNjY1MjIzNjYx&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=1080" alt="Blue clock on a pastel background" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@icons8?utm_source=Papyrs&amp;utm_medium=referral" rel="noopener noreferrer external">Icons8 Team</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>Some use cases require frontend dapp to repeatedly calls a function deployed on the <a href="https://internetcomputer.org/" rel="noopener noreferrer external">Internet Computer</a> with a fixed time delay between each call. e.g. in <a href="https://papy.rs" rel="noopener noreferrer external">Papyrs</a> - a web3 blogging platform - there is no save button. Each edit users are making is automatically saved in the backend.</p>
<p>Likewise, some other application might require information to be pushed from a server rather than being queried. e.g. using web socket to implement a chat. However, such type of protocol or feature is not yet supported by canister smart contracts.</p>
<p>Commonly I solve these two problems by creating cronjob that runs in my frontend dapp - i.e. in the browser.</p>
<p>While not all such functions might decrease the performance of the UI, I like to be precautious about it. That is why I always defer the job of these schedulers to <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers" rel="noopener noreferrer external">web workers</a>.</p>
<p>In this article, I will show you how you can implement such a solution too.</p>
<hr />
<h2>Web workers?</h2>
<p>If you are not familiar with web workers you might ask yourself what it is. I advice you to have a look to the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers" rel="noopener noreferrer external">MDN documentation</a> for all the details but, in few words, web workers are a simple way to run scripts in the background threads of the browser.</p>
<p>They can run JavaScript and perform HTTP calls but, they cannot access the localstorage nor modify the DOM directly. e.g. you cannot modify a state in a web worker and expect the UI to be re-rendered automatically. UI (window) and workers are separated. You can picture this a bit like the separation caused by embedding an iframe.</p>
<hr />
<h2>Introduction</h2>
<p>The solution I aim to display defers the work from the UI to web workers. When the application starts, it initializes and starts the polling which take care of querying the IC repeatedly. Ultimately, to render the results in the UI, the backend threads transfer the data to the window of the browser.</p>
<p><img src="https://6zvwc-sqaaa-aaaal-aalma-cai.raw.ic0.app/images/excalidraw-1665226130679.webp?token=kr6uPPnKwLhWMNrU_ycNK" alt="https://6zvwc-sqaaa-aaaal-aalma-cai.raw.ic0.app/images/excalidraw-1665226130679.webp?token=kr6uPPnKwLhWMNrU_ycNK" / loading="lazy"></p>
<p>The tutorial covers therefore following steps:</p>
<ol>
<li>Set up and start a web worker</li>
<li>Use <a href="https://github.com/dfinity/agent-js/" rel="noopener noreferrer external">agent-js</a> in the worker to get the identity</li>
<li>Call the IC</li>
<li>Transfer the results to the UI</li>
</ol>
<hr />
<h2>1. Set up a web worker</h2>
<p>For this tutorial I will use vanilla JavaScript because when you create a new sample application with <code>dfx</code> or <code>npm init ic</code>, the outcome is a barebone frontend app.</p>
<p>But, I have got your back 😄. I am a HUGE fan of web workers and I published various tutorials that show how to set up these for various frameworks.</p>
<ul>
<li><a href="https://6zvwc-sqaaa-aaaal-aalma-cai.raw.ic0.app/d/sveltekit-web-worker" rel="noopener noreferrer external">SvelteKit</a></li>
<li><a href="https://daviddalbusco.com/blog/angular-and-web-workers/" rel="noopener noreferrer external">Angular</a></li>
<li><a href="https://daviddalbusco.com/blog/stenciljs-and-web-worker-a-fairy-tale/" rel="noopener noreferrer external">Stencil</a></li>
</ul>
<p><em>I also got a post about the integration in <a href="https://daviddalbusco.com/blog/react-and-web-workers/" rel="noopener noreferrer external">React</a> but, it is a bit outdated. If you use Webpack, better follow this article.</em></p>
<p>So, assuming you have also initialized a sample application to follow this tutorial, we can start by creating the worker in a new file. e.g. <code>src/myapp\_frontend/src/worker.js</code>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">self.</span><span style="color:#50FA7B">onmessage</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">$event</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Worker message</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, $event);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>In above snippet and at this point, the worker just listens for messages and print these to the console.</p>
<p>On the UI side, we declare two elements. A <code>button</code> to sign in with <a href="https://identity.ic0.app/" rel="noopener noreferrer external">Internet Identity</a> and a <code>textarea</code> use ultimately to render the results we are polling.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">main</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">button</span><span style="color:#50FA7B;font-style:italic"> type</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">button</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Sign in&#x3C;/</span><span style="color:#FF79C6">button</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">textarea</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">/</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">main</span><span style="color:#F8F8F2">></span></span></code></pre><p>We do not absolutely need to sign in to query the IC with web workers or generally speaking but, it is probably a common use case. That is why this post showcases it as well.</p>
<p>Once the HTML document has been completely parsed, we attach a <code>click</code> event to the button that performs a sign in with II.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { AuthClient } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@dfinity/auth-client</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> signIn</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> authClient </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> AuthClient.</span><span style="color:#50FA7B">create</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#F8F8F2"> authClient.</span><span style="color:#50FA7B">login</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#50FA7B">        onSuccess</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">await</span><span style="color:#F8F8F2"> authClient.</span><span style="color:#50FA7B">isAuthenticated</span><span style="color:#F8F8F2">()),</span></span>
<span class="line"><span style="color:#50FA7B">        onError</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">err</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(err),</span></span>
<span class="line"><span style="color:#F8F8F2">        identityProvider</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> `http://r7inp-6aaaa-aaaaa-aaabq-cai.localhost:8000?#authorize`</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> initSignInButton</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> button </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> document.</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">button</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    button.</span><span style="color:#50FA7B">addEventListener</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">click</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, signIn, { passive</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2"> });</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> init</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    initSignInButton</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">document.</span><span style="color:#50FA7B">addEventListener</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">DOMContentLoaded</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, init);</span></span></code></pre><p>To set up and start the worker, we add an additional function that creates a <code>Worker</code> object, declares a listener used to receive the results and post a message from the UI to the worker to start the polling.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> startWorker</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> worker </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Worker</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6;font-weight:bold">new</span><span style="color:#50FA7B"> URL</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">./worker.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#FF79C6">import</span><span style="color:#F8F8F2">.meta.url));</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    worker.</span><span style="color:#50FA7B">onmessage</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">$event</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Window message</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, $event);</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    worker.</span><span style="color:#50FA7B">postMessage</span><span style="color:#F8F8F2">({ msg</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">start</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> });</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> init</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    startWorker</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#50FA7B">    initSignInButton</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>If we deploy the solution to a local simulated network, we should be able to sign in and when the dapp starts, the worker should print the &quot;start&quot; message.</p>
<p><img src="https://6zvwc-sqaaa-aaaal-aalma-cai.raw.ic0.app/images/capture-d%E2%80%99e%CC%81cran-2022-10-08-a%CC%80-13.29.22.png?token=PDzwmLoijNmdCbHsGBhHk" alt="capture-d%E2%80%99e%CC%81cran-2022-10-08-a%CC%80-13.29.22.png" / loading="lazy"></p>
<hr />
<h2>2. Use agent-js in the worker to get the identity</h2>
<p>I like to provide both a &quot;start&quot; and &quot;stop&quot; options when I implement this type of cronjob. In this particular example we won't use the &quot;stop&quot; feature but, it can be particularly useful when the worker is created from a component. It can be used to stop the timer when the component gets unmounted.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">let</span><span style="color:#F8F8F2"> timer;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">self.</span><span style="color:#50FA7B">onmessage</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">data</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { msg } </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> data;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    switch</span><span style="color:#F8F8F2"> (msg) {</span></span>
<span class="line"><span style="color:#FF79C6">        case</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">start</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">:</span></span>
<span class="line"><span style="color:#50FA7B">            start</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">            break</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">        case</span><span style="color:#F8F8F2"> stop:</span></span>
<span class="line"><span style="color:#50FA7B">            stop</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> stop</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> clearInterval</span><span style="color:#F8F8F2">(timer);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> start</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> (timer </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> setInterval</span><span style="color:#F8F8F2">(call, </span><span style="color:#BD93F9">2000</span><span style="color:#F8F8F2">));</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> call</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#6272A4">    // TODO: call the IC</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>The &quot;start&quot; method instantiates a <code>setInterval</code> which will repeatedly call the function that queries the IC. In this particular function, we create an authentication client with agent-js to retrieve the identity of the user if signed in.</p>
<p>While the initialization look similar to what we commonly do on the UI side (kudos to <a href="https://forum.dfinity.org/u/sea-snake" rel="noopener noreferrer external">Sea Snake</a> for the inputs about it on <a href="https://discord.com/invite/cA7y6ezyE2" rel="noopener noreferrer external">Discord</a>), it is important to note that the &quot;idle manager&quot; feature of the client must be disabled. This feature detects users' inactivity by observing <code>window</code> events. The web worker not being able to read such object, not disabling these capabilities would lead to errors.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> call</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#6272A4">    // Disable idle manager because web worker cannot access the window object of the UI</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> authClient </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> AuthClient.</span><span style="color:#50FA7B">create</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        idleOptions</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">            disableIdle</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">            disableDefaultIdleCallback</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> isAuthenticated </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> authClient.</span><span style="color:#50FA7B">isAuthenticated</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">isAuthenticated) {</span></span>
<span class="line"><span style="color:#6272A4">        // User is not authenticated</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> identity </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> authClient.</span><span style="color:#50FA7B">getIdentity</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // TODO: call the IC</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><hr />
<h2>3. Call the IC</h2>
<p>Because of the same limitation as in previous chapter, we have to provide a <code>host</code> information to create an actor that calls the backend. Moreover, because the declarations automatically generated by dfx contain a default actor that is only designed to work on the UI side, we have to copy the function that initializes an actor - and the canister ID constant - within our worker or in a dedicated file.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">// Copy from auto generated declarations</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> canisterId </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> process.env.</span><span style="color:#BD93F9">ICWEBWORKER_BACKEND_CANISTER_ID</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> createActor</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">canisterId</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">options</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> agent </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> HttpAgent</span><span style="color:#F8F8F2">(options </span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2"> { </span><span style="color:#FF79C6">...</span><span style="color:#F8F8F2">options.agentOptions } </span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // Fetch root key for certificate validation during development</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (process.env.</span><span style="color:#BD93F9">NODE_ENV</span><span style="color:#FF79C6"> !==</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">production</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">        agent.</span><span style="color:#50FA7B">fetchRootKey</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">catch</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">err</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">            console.</span><span style="color:#50FA7B">warn</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Unable to fetch root key. Check to ensure that your local replica is running</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">            console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(err);</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // Creates an actor with using the candid interface and the HttpAgent</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> Actor.</span><span style="color:#50FA7B">createActor</span><span style="color:#F8F8F2">(idlFactory, {</span></span>
<span class="line"><span style="color:#F8F8F2">        agent,</span></span>
<span class="line"><span style="color:#F8F8F2">        canisterId,</span></span>
<span class="line"><span style="color:#FF79C6">        ...</span><span style="color:#F8F8F2">(options </span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2"> options.actorOptions </span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {})</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Once copied, we instantiate the actor and effectively perform the call.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> query</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">identity</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> actor </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> createActor</span><span style="color:#F8F8F2">(canisterId, {</span></span>
<span class="line"><span style="color:#F8F8F2">        agentOptions</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { identity, host</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> `http://</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">canisterId</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">.localhost:8000/`</span><span style="color:#F8F8F2"> }</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> greeting </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> actor.</span><span style="color:#50FA7B">greet</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // TODO: transfer results to UI</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>To load the web worker in the browser and to fetch the root key in development, we have to access the local server and provide the host with the canister ID as a subdomain (not query parameter).</p>
<p>For production, the host parameter can be replaced with <code>https://ic0.app</code>. This works out regardless of your frontend and backend effective URLs.</p>
<p>Not strictly related to this solution, here is the backend code that gets called - i.e. the <code>greet</code> function I implemented for demo purpose.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> Principal </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">mo:base/Principal</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">import Nat </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">mo:base/Nat</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">actor {</span></span>
<span class="line"><span style="color:#F8F8F2">  stable var counter = 0;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  public shared({caller}) func greet() : async Text {</span></span>
<span class="line"><span style="color:#F8F8F2">    counter += 1;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    return </span><span style="color:#F1FA8C">"Hello, "</span><span style="color:#F8F8F2"> # Principal.toText(caller) # </span><span style="color:#F1FA8C">". Counter: "</span><span style="color:#F8F8F2"> # Nat.toText(counter);</span></span>
<span class="line"><span style="color:#F8F8F2">  };</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>It is a simple update method that increments a counter and returns &quot;Hello&quot;, the caller principal ID as text and the incremented value.</p>
<hr />
<h2>4. Transfer the results to the UI</h2>
<p>Now that we have obtained data from the IC, we can apply these to re-render the UI. As the worker itself cannot do so, we transfer the data to the view with the help of <a href="https://developer.mozilla.org/en-US/docs/Web/API/Worker/postMessage" rel="noopener noreferrer external">postMessage</a>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> query</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">identity</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> actor </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> createActor</span><span style="color:#F8F8F2">(canisterId, {</span></span>
<span class="line"><span style="color:#F8F8F2">        agentOptions</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { identity, host</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> `http://</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">canisterId</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">.localhost:8000/`</span><span style="color:#F8F8F2"> }</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> greeting </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> actor.</span><span style="color:#50FA7B">greet</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // Transfer data worker -> window / UI</span></span>
<span class="line"><span style="color:#50FA7B">    postMessage</span><span style="color:#F8F8F2">({ msg</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">result</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, greeting });</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>In the first chapter we have already declared a listener for the messages received from the worker. Therefore, to finalize the solution, we process the data and update the DOM.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">// In index.js - in the view</span></span>
<span class="line"><span style="color:#F8F8F2">worker.</span><span style="color:#50FA7B">onmessage</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">data</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { msg, greeting } </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> data;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    switch</span><span style="color:#F8F8F2"> (msg) {</span></span>
<span class="line"><span style="color:#FF79C6">        case</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">result</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">:</span></span>
<span class="line"><span style="color:#F8F8F2">            document.</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">textarea</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">).value </span><span style="color:#FF79C6">+=</span><span style="color:#F1FA8C"> `</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">greeting</span><span style="color:#FF79C6">}\n</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Above code snippet appends the data - the results provided by the worker, the data returned by the backend - to a textarea. If we deploy to a local simulated IC and open the dapp in a browser, we should notice text being added to the element every two seconds.</p>
<p><img src="https://6zvwc-sqaaa-aaaal-aalma-cai.raw.ic0.app/images/icwebworker.gif?token=ydvGykqt737dhu_kPIuQV" alt="icwebworker.gif" / loading="lazy"></p>
<hr />
<h2>Summary</h2>
<p>Thanks to web worker we can poll the Internet Computer without compromising on the frontend performance of our dapp. I ❤️ it.</p>
<p>You will find this solution in a sample repo I shared on GitHub 👉 <a href="https://github.com/peterpeterparker/icwebworker" rel="noopener noreferrer external">https://github.com/peterpeterparker/icwebworker</a></p>
<p>To infinity and beyond<br />
David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Prepare your dapps for social platform and SEO]]></title>
          <description><![CDATA[A guide to set up dapps metadata, icons and social image for social media sites and search engines.]]></description>
          <link>https://daviddalbusco.com/blog/prepare-your-dapps-for-social-platform-and-seo/</link>
          <pubDate>Thu, 29 Sep 2022 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://images.unsplash.com/photo-1622547748225-3fc4abd2cca0?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwzMDkyMzV8MHwxfHNlYXJjaHw0Nnx8YWJzdHJhY3R8ZW58MHx8fHwxNjY0NDI4NDU5&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=1080" alt="Minimalistic 3D Rendering Wallpaper in 8K Resolution." / loading="lazy"></p>
<p><em><a href="https://unsplash.com/@sebastiansvenson" rel="noopener noreferrer external">Sebastian Svenson</a></em></p>
<p>Now that the <a href="https://internetcomputer.org/" rel="noopener noreferrer external">Internet Computer</a> finally supports crawlers, dapps running 100% on chain can be indexed by search engine and their metadata can be read to generate card on social platform.</p>
<p>This is the list of things I do to prepare my web applications for social media sites and SEO so that you can do too.</p>
<p><em>Note that I am no SEO expert. Following tips are not related to ranking but to the way the information are extracted for presentation purpose.</em></p>
<hr />
<h2>Introduction</h2>
<p>To configure the bare minimum meta data for a project, we need following information to get started:</p>
<ul>
<li>Title: the name of the application, the product.</li>
<li>Description: its tag line, its catchy selling phrase.</li>
<li>Icon: a square image used to generate the favicons (note the plural).</li>
<li>Social image: another image (ratio 1.9:1) used to generate a card on platforms such as Twitter, Facebook, Discord, LinkedIn etc.</li>
</ul>
<hr />
<h2>Meta tags</h2>
<p>Search engines use the content they crawl for indexation, ranking and rendering purpose but, my understanding, still consider the meta tags available in the <code>HTML</code> for search indexation too.</p>
<p>Likewise, crawlers of social platform look for meta tags to generate the social media content displayed for any link that is shared.</p>
<p>That is why, we have to provide a variety of meta tags and images for our dapps.</p>
<hr />
<h3>HTML tags</h3>
<p>The most obvious set of information we can define is probably the HTML elements in the <code>&lt;head /&gt;</code> container of our HTML pages.</p>
<p>We can - or should - set a <code>&lt;title /&gt;</code> but also a <code>&lt;description /&gt;</code>. Sometimes I also provide an <code>&lt;author /&gt;</code>.</p>
<p>Two others things that should really not be forgotten:</p>
<ol>
<li>It is important to set a language to the document that matches its content. Not absolutely related to this tutorial but if you provide e.g. meta tags in Spanish, it is useful to also set the document as such.</li>
<li>If your content is redundant on the web - e.g. blogging on multiple platforms - or if you want to indicate that multiple pages are actually related, it is important to provide <code>canonical</code> links to inform search engine that they should not index multiple times the same content. Otherwise, it can lead to a downgrade of the ranking note (my understanding).</li>
</ol>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;!</span><span style="color:#FF79C6">doctype</span><span style="color:#50FA7B;font-style:italic"> html</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">html</span><span style="color:#50FA7B;font-style:italic"> lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">en</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">head</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">meta</span><span style="color:#50FA7B;font-style:italic"> charset</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">utf-8</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">title</span><span style="color:#F8F8F2">>Papyrs&#x3C;/</span><span style="color:#FF79C6">title</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">meta</span><span style="color:#50FA7B;font-style:italic"> content</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Blog on web3</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> name</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">description</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">link</span><span style="color:#50FA7B;font-style:italic"> href</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://papy.rs</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> rel</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">canonical</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">head</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">html</span><span style="color:#F8F8F2">></span></span></code></pre><p>Resources:</p>
<ul>
<li><a href="https://www.w3schools.com/html/html_head.asp" rel="noopener noreferrer external">https://www.w3schools.com/html/html_head.asp</a></li>
<li><a href="https://developers.google.com/search/docs/crawling-indexing/consolidate-duplicate-urls" rel="noopener noreferrer external">https://developers.google.com/search/docs/crawling-indexing/consolidate-duplicate-urls</a></li>
</ul>
<hr />
<h2>og: tags</h2>
<p>Open Graph meta tags - or <code>og:</code> - are snippets of code that control how our applications are displayed when shared on social platform.</p>
<p>i.e. the tags control what will be displayed when our URLs are shared on platform such as Facebook, LinkedIn, Discord, etc.</p>
<p>There are lots of these different tags but, commonly I use only the following five in my sites and applications:</p>
<ol>
<li><code>og:title</code>: the title of the app or in case of multiple pages, a title related to the page's content - e.g. in case of a blog, the particular title of a post.</li>
<li><code>og:description</code>: same as title but for the description.</li>
<li><code>og:url</code>: the URL of the content. To consolidate connected data, I also apply here the same approach as for the <code>canonical</code> URLs.</li>
<li><code>og:type</code>: commonly <code>website</code> for a page or <code>article</code> in case of a blog post.</li>
<li><code>og:image</code>: an absolute URL to the social image (see chapter here under for more information about its format).</li>
</ol>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;!</span><span style="color:#FF79C6">doctype</span><span style="color:#50FA7B;font-style:italic"> html</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">html</span><span style="color:#50FA7B;font-style:italic"> lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">en</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">head</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#6272A4">        &#x3C;!-- Here same HTML tags as previous chapter --></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">meta</span><span style="color:#50FA7B;font-style:italic"> content</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Papyrs</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> property</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">og:title</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">meta</span><span style="color:#50FA7B;font-style:italic"> content</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Blog on web3</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> property</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">og:description</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">meta</span><span style="color:#50FA7B;font-style:italic"> content</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">website</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> property</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">og:type</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">meta</span><span style="color:#50FA7B;font-style:italic"> content</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://papy.rs</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> property</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">og:url</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">meta</span><span style="color:#50FA7B;font-style:italic"> content</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://papy.rs/images/social-image.jpg</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> property</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">og:image</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">head</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">html</span><span style="color:#F8F8F2">></span></span></code></pre><p>Resources:</p>
<ul>
<li><a href="https://ogp.me/" rel="noopener noreferrer external">https://ogp.me/</a></li>
</ul>
<p><img src="https://6zvwc-sqaaa-aaaal-aalma-cai.raw.ic0.app/images/capture-d%E2%80%99e%CC%81cran-2022-09-29-a%CC%80-12.01.15.png?token=zmeCyAslDbC4Yc5k8Hqlo" alt="capture-d%E2%80%99e%CC%81cran-2022-09-29-a%CC%80-12.01.15.png" / loading="lazy"></p>
<p><em>Sharing https://papy.rs on LinkedIn</em></p>
<hr />
<h3>twitter: tags</h3>
<p>While Open Graph tags are interpreted as well (I think), Twitter has its own set of meta tags. That is why it is best to also provide these to control how our applications are displayed when shared on this particular platform.</p>
<p>There are various possibilities of display, I generally set following property to render the &quot;summary cards&quot;:</p>
<ol>
<li><code>twitter:card</code>: the type of card - of tweet. I like to use <code>summary_large_image</code> because it fits best the size of the social images I provide.</li>
<li><code>twitter:title</code>: the title of the product or page.</li>
<li><code>twitter:description</code>: the catchy phrase.</li>
<li><code>twitter:image</code>: the absolute URL to the social image.</li>
<li><code>twitter:creator</code>: the Twitter handle of the product or its creator.</li>
</ol>
<p>It is worth to remember that a tweet limit is 280 characters which apply to the information we provide.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;!</span><span style="color:#FF79C6">doctype</span><span style="color:#50FA7B;font-style:italic"> html</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">html</span><span style="color:#50FA7B;font-style:italic"> lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">en</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">head</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#6272A4">        &#x3C;!-- Here same HTML tags as previous chapter --></span></span>
<span class="line"><span style="color:#6272A4">        &#x3C;!-- Here same og: tags as previous chapter --></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">meta</span><span style="color:#50FA7B;font-style:italic"> content</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">summary_large_image</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> name</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">twitter:card</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">meta</span><span style="color:#50FA7B;font-style:italic"> content</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Papyrs</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> name</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">twitter:title</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">meta</span><span style="color:#50FA7B;font-style:italic"> content</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Blog on web3</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> name</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">twitter:description</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">meta</span><span style="color:#50FA7B;font-style:italic"> content</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://papy.rs/images/social-image.jpg</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> name</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">twitter:image</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">meta</span><span style="color:#50FA7B;font-style:italic"> content</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">@PapyrsApp</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> name</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">twitter:creator</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">head</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">html</span><span style="color:#F8F8F2">></span></span></code></pre><p>Resources:</p>
<ul>
<li><a href="https://developer.twitter.com/en/docs/twitter-for-websites/cards/overview/summary-card-with-large-image" rel="noopener noreferrer external">https://developer.twitter.com/en/docs/twitter-for-websites/cards/overview/summary-card-with-large-image</a></li>
</ul>
<p><img src="https://6zvwc-sqaaa-aaaal-aalma-cai.raw.ic0.app/images/capture-d%E2%80%99e%CC%81cran-2022-09-29-a%CC%80-12.03.37.png?token=AoqEPYOZe9maYX2R_W39X" alt="capture-d%E2%80%99e%CC%81cran-2022-09-29-a%CC%80-12.03.37.png" / loading="lazy"></p>
<p><em>Sharing https://papy.rs on Twitter</em></p>
<hr />
<h2>Social image</h2>
<p>Providing the social image is governed by one single rule: it should be provided with an absolute URL - i.e. it should not be provided with an absolute path. It would not be interpreted correctly by crawlers.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">&#x3C;!-- This works --></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">meta</span><span style="color:#50FA7B;font-style:italic"> content</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://papy.rs/images/social-image.jpg</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> name</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">twitter:image</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">&#x3C;!-- This does NOT work --></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">meta</span><span style="color:#50FA7B;font-style:italic"> content</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/images/social-image.jpg</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> name</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">twitter:image</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span></code></pre><p>Regarding format and content and as a best practice, I advise to create an image of <code>1200x628</code> pixels for optimal dimension across all devices and to use an old fashioned image format such as <code>png</code> or <code>jpg</code>.</p>
<p>Speaking of, it will be displayed on small and large screen - i.e. there is only one single image for all screens. Therefore, when designing it, it is also worth to remember to compose its content so that it fits best anywhere.</p>
<hr />
<h2>Favicons</h2>
<p>Favicons aren't just the good old <code>favicon.ico</code> we used to set at the root of our website back in the good old days (😉). Sure, we should still provide one favorite icon for the browser but, we also have to think on various devices. Vendors have added various ways of defining icons that will be use when our applications and sites get added to the device' home screen.</p>
<p>In addition, there are also some new meta tags that we can set to specify other capabilities. e.g. defining the theme color that will be applied around the browser URL bar on mobile devices.</p>
<p>To generate these data I proceed as following:</p>
<ol>
<li>I design an icon which I export to <code>png</code> or <code>jpg</code> in a square format - e.g. <code>1080x1080</code> pixels.</li>
<li>I head up to <a href="https://realfavicongenerator.net/" rel="noopener noreferrer external">https://realfavicongenerator.net/</a> and I use this tool to generate a set of data for favicons and theming information.</li>
<li>While I could stop here, since above tool already generates all data needed, I like to overrule those icons it generated to provide more flavors - my own icons. That's why I export various dimension of my icons (<code>48x48</code>, <code>72x72</code>, <code>96x96</code>, <code>144x144</code>, <code>192x192</code>, <code>256x256</code>, <code>384x384</code> and <code>512x512</code> pixels).</li>
<li>I copy my icons and the generated data to the static assets of my applications.</li>
<li>Finally, I set the related meta tags to my HTML pages.</li>
</ol>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;!</span><span style="color:#FF79C6">doctype</span><span style="color:#50FA7B;font-style:italic"> html</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">html</span><span style="color:#50FA7B;font-style:italic"> lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">en</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">head</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#6272A4">        &#x3C;!-- Here same HTML tags as previous chapter --></span></span>
<span class="line"><span style="color:#6272A4">        &#x3C;!-- Here same og: tags as previous chapter --></span></span>
<span class="line"><span style="color:#6272A4">        &#x3C;!-- Here same twitter: tags as previous chapter --></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">link</span><span style="color:#50FA7B;font-style:italic"> href</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/favicon-32x32.png</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> rel</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">icon</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> type</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">image/png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">meta</span><span style="color:#50FA7B;font-style:italic"> content</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">#000000</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> name</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">theme-color</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">link</span><span style="color:#50FA7B;font-style:italic"> href</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/icons/icon-48x48.png</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> rel</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">apple-touch-icon</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> sizes</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">48x48</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">link</span><span style="color:#50FA7B;font-style:italic"> href</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/icons/icon-72x72.png</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> rel</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">apple-touch-icon</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> sizes</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">72x72</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">link</span><span style="color:#50FA7B;font-style:italic"> href</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/icons/icon-96x96.png</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> rel</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">apple-touch-icon</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> sizes</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">96x96</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">link</span><span style="color:#50FA7B;font-style:italic"> href</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/icons/icon-144x144.png</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> rel</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">apple-touch-icon</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> sizes</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">144x144</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">link</span><span style="color:#50FA7B;font-style:italic"> href</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/icons/icon-192x192.png</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> rel</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">apple-touch-icon</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> sizes</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">192x192</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">link</span><span style="color:#50FA7B;font-style:italic"> href</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/icons/icon-256x256.png</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> rel</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">apple-touch-icon</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> sizes</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">256x256</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">link</span><span style="color:#50FA7B;font-style:italic"> href</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/icons/icon-384x384.png</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> rel</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">apple-touch-icon</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> sizes</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">384x384</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">link</span><span style="color:#50FA7B;font-style:italic"> href</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/icons/icon-512x512.png</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> rel</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">apple-touch-icon</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> sizes</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">512x512</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">link</span><span style="color:#50FA7B;font-style:italic"> rel</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">mask-icon</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> href</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/icons/safari-pinned-tab.svg</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> color</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">#7888ff</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">meta</span><span style="color:#50FA7B;font-style:italic"> name</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">msapplication-TileColor</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> content</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">#ffeed6</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">meta</span><span style="color:#50FA7B;font-style:italic"> name</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">msapplication-config</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> content</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/icons/browserconfig.xml</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">head</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">html</span><span style="color:#F8F8F2">></span></span></code></pre><p>Resources:</p>
<ul>
<li><a href="https://realfavicongenerator.net/" rel="noopener noreferrer external">https://realfavicongenerator.net/</a></li>
</ul>
<p><img src="https://6zvwc-sqaaa-aaaal-aalma-cai.raw.ic0.app/images/capture-d%E2%80%99e%CC%81cran-2022-09-29-a%CC%80-12.10.11.png?token=ZDNI5SzJk2WsjoE9z9gDD" alt="capture-d%E2%80%99e%CC%81cran-2022-09-29-a%CC%80-12.10.11.png" / loading="lazy"></p>
<p><em>A favicon displayed in the tab by Firefox</em></p>
<hr />
<h3>Maskable icons</h3>
<p>While above does the job for most devices, for Android devices it is best to use adaptive icons - also know as &quot;maskable icons&quot; - as well. They display app icons in a variety of shapes across different device models.</p>
<p>I generally create another icon which I shape with the help of <a href="https://maskable.app/editor" rel="noopener noreferrer external">https://maskable.app/editor</a> to fits best any devices and which I also export in all various dimension I listed in previous chapter.</p>
<p>Resources:</p>
<ul>
<li><a href="https://web.dev/maskable-icon/" rel="noopener noreferrer external">https://web.dev/maskable-icon/</a></li>
<li><a href="https://maskable.app/editor" rel="noopener noreferrer external">https://maskable.app/editor</a></li>
</ul>
<p><img src="https://6zvwc-sqaaa-aaaal-aalma-cai.raw.ic0.app/images/maskable-animation.gif?token=Np_L4Zk4hIMEYArteyU00" alt="maskable-animation.gif" / loading="lazy"></p>
<hr />
<h2>Web app manifest</h2>
<p>The web app manifest is a JSON file that tells the browser about our web application and how it should behave when installed on the user's desktop or mobile device.</p>
<p>It should be provided by our smart contract with the appropriate JSON mime type (<code>application/json</code>).</p>
<p>Commonly named <code>manifest.webmanifest</code> or <code>manifest.json</code> and served from the root of our websites, these files contain the same meta information as those we specified previously but, can also provide much more - e.g. shortcuts and screenshots.</p>
<p>Even if provided on a top-level directory, it should be referenced in the HTML pages.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;!</span><span style="color:#FF79C6">doctype</span><span style="color:#50FA7B;font-style:italic"> html</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">html</span><span style="color:#50FA7B;font-style:italic"> lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">en</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">head</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#6272A4">        &#x3C;!-- Here same HTML tags as previous chapter --></span></span>
<span class="line"><span style="color:#6272A4">        &#x3C;!-- Here same og: tags as previous chapter --></span></span>
<span class="line"><span style="color:#6272A4">        &#x3C;!-- Here same twitter: tags as previous chapter --></span></span>
<span class="line"><span style="color:#6272A4">        &#x3C;!-- Here same favicons and other info as previous chapter --></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">link</span><span style="color:#50FA7B;font-style:italic"> crossorigin</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">anonymous</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> href</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/manifest.webmanifest</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> rel</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">manifest</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">head</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">html</span><span style="color:#F8F8F2">></span></span></code></pre><p>The tool I listed previously - Favicon Generator - provides a bare minimal web app manifest which I enhance with my custom icons.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">name</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Papyrs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">short_name</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Papyrs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">start_url</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">/</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">background_color</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">#000000</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">theme_color</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">#000000</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">display</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">standalone</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">icons</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#F8F8F2">        { </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">src</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">icons/icon-48x48.png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">sizes</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">48x48</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">type</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">image/png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">purpose</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">any</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> },</span></span>
<span class="line"><span style="color:#F8F8F2">        { </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">src</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">icons/icon-72x72.png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">sizes</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">72x72</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">type</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">image/png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">purpose</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">any</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> },</span></span>
<span class="line"><span style="color:#F8F8F2">        { </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">src</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">icons/icon-96x96.png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">sizes</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">96x96</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">type</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">image/png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">purpose</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">any</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> },</span></span>
<span class="line"><span style="color:#F8F8F2">        { </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">src</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">icons/icon-144x144.png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">sizes</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">144x144</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">type</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">image/png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">purpose</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">any</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> },</span></span>
<span class="line"><span style="color:#F8F8F2">        { </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">src</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">icons/icon-192x192.png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">sizes</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">192x192</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">type</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">image/png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">purpose</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">any</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> },</span></span>
<span class="line"><span style="color:#F8F8F2">        { </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">src</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">icons/icon-256x256.png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">sizes</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">256x256</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">type</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">image/png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">purpose</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">any</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> },</span></span>
<span class="line"><span style="color:#F8F8F2">        { </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">src</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">icons/icon-384x384.png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">sizes</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">384x384</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">type</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">image/png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">purpose</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">any</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> },</span></span>
<span class="line"><span style="color:#F8F8F2">        { </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">src</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">icons/icon-512x512.png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">sizes</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">512x512</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">type</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">image/png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">purpose</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">any</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> },</span></span>
<span class="line"><span style="color:#F8F8F2">        {</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">src</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">icons/maskable-48x48.png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">sizes</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">48x48</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">type</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">image/png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">purpose</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">maskable</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">        },</span></span>
<span class="line"><span style="color:#F8F8F2">        {</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">src</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">icons/maskable-72x72.png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">sizes</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">72x72</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">type</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">image/png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">purpose</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">maskable</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">        },</span></span>
<span class="line"><span style="color:#F8F8F2">        {</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">src</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">icons/maskable-96x96.png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">sizes</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">96x96</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">type</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">image/png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">purpose</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">maskable</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">        },</span></span>
<span class="line"><span style="color:#F8F8F2">        {</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">src</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">icons/maskable-144x144.png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">sizes</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">144x144</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">type</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">image/png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">purpose</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">maskable</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">        },</span></span>
<span class="line"><span style="color:#F8F8F2">        {</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">src</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">icons/maskable-192x192.png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">sizes</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">192x192</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">type</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">image/png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">purpose</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">maskable</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">        },</span></span>
<span class="line"><span style="color:#F8F8F2">        {</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">src</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">icons/maskable-256x256.png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">sizes</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">256x256</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">type</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">image/png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">purpose</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">maskable</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">        },</span></span>
<span class="line"><span style="color:#F8F8F2">        {</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">src</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">icons/maskable-384x384.png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">sizes</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">384x384</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">type</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">image/png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">purpose</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">maskable</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">        },</span></span>
<span class="line"><span style="color:#F8F8F2">        {</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">src</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">icons/maskable-512x512.png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">sizes</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">512x512</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">type</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">image/png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">purpose</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">maskable</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    ]</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Resources:</p>
<ul>
<li><a href="https://web.dev/add-manifest/" rel="noopener noreferrer external">https://web.dev/add-manifest/</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/Manifest" rel="noopener noreferrer external">https://developer.mozilla.org/en-US/docs/Web/Manifest</a></li>
</ul>
<hr />
<h2>Sitemap.xml</h2>
<p>Sitemaps are useful to help search engines understand what pages should be crawled and indexed. While I am guessing it is not that useful in case of single page application, I like to provide the information anyway.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;?</span><span style="color:#FF79C6">xml</span><span style="color:#50FA7B;font-style:italic"> version</span><span style="color:#F8F8F2">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">1.0</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> encoding</span><span style="color:#F8F8F2">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">UTF-8</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> ?></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">urlset</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    xmlns</span><span style="color:#F8F8F2">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">http://www.sitemaps.org/schemas/sitemap/0.9</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    xmlns</span><span style="color:#FF79C6;font-style:italic">:</span><span style="color:#50FA7B;font-style:italic">news</span><span style="color:#F8F8F2">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">http://www.google.com/schemas/sitemap-news/0.9</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    xmlns</span><span style="color:#FF79C6;font-style:italic">:</span><span style="color:#50FA7B;font-style:italic">xhtml</span><span style="color:#F8F8F2">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">http://www.w3.org/1999/xhtml</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    xmlns</span><span style="color:#FF79C6;font-style:italic">:</span><span style="color:#50FA7B;font-style:italic">image</span><span style="color:#F8F8F2">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">http://www.google.com/schemas/sitemap-image/1.1</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    xmlns</span><span style="color:#FF79C6;font-style:italic">:</span><span style="color:#50FA7B;font-style:italic">video</span><span style="color:#F8F8F2">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">http://www.google.com/schemas/sitemap-video/1.1</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">url</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">loc</span><span style="color:#F8F8F2">>https://papy.rs/&#x3C;/</span><span style="color:#FF79C6">loc</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">changefreq</span><span style="color:#F8F8F2">>monthly&#x3C;/</span><span style="color:#FF79C6">changefreq</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">priority</span><span style="color:#F8F8F2">>0.7&#x3C;/</span><span style="color:#FF79C6">priority</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">url</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">urlset</span><span style="color:#F8F8F2">></span></span></code></pre><p>The <code>sitemap.xml</code> is an XML file, therefore it should be served by our canister with the appropriate mime type (<code>application/xml</code>). It should also be referenced within our HTML pages.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;!</span><span style="color:#FF79C6">doctype</span><span style="color:#50FA7B;font-style:italic"> html</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">html</span><span style="color:#50FA7B;font-style:italic"> lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">en</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">head</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#6272A4">        &#x3C;!-- Here same HTML tags as previous chapter --></span></span>
<span class="line"><span style="color:#6272A4">        &#x3C;!-- Here same og: tags as previous chapter --></span></span>
<span class="line"><span style="color:#6272A4">        &#x3C;!-- Here same twitter: tags as previous chapter --></span></span>
<span class="line"><span style="color:#6272A4">        &#x3C;!-- Here same favicons and other info as previous chapter --></span></span>
<span class="line"><span style="color:#6272A4">        &#x3C;!-- Here link to web app manifest --></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">link</span><span style="color:#50FA7B;font-style:italic"> href</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/sitemap.xml</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> rel</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">sitemap</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> type</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">application/xml</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">head</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">html</span><span style="color:#F8F8F2">></span></span></code></pre><p>Resources:</p>
<ul>
<li><a href="https://developers.google.com/search/docs/crawling-indexing/sitemaps/build-sitemap" rel="noopener noreferrer external">https://developers.google.com/search/docs/crawling-indexing/sitemaps/build-sitemap</a></li>
</ul>
<hr />
<p>Robots.txt</p>
<p>At this point we set up everything we need for search engine and social platform but, crawlers might not yet be able to read these information. That is why we need to add a <code>robots.txt</code> file at the root of our site to control how they should access our content.</p>
<p>Assuming we want any crawlers to index our all app, we can provide such information by targeting all <code>User-agent: \*</code> and allowing everything <code>Allow: /</code>.</p>
<p>We can also provide again the URL to our sitemap and host information.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">User-agent: * Allow: / Sitemap: https://papy.rs/sitemap.xml Host: https://papy.rs</span></span></code></pre><p>Resources:</p>
<ul>
<li><a href="https://developers.google.com/search/docs/crawling-indexing/robots/create-robots-txt" rel="noopener noreferrer external">https://developers.google.com/search/docs/crawling-indexing/robots/create-robots-txt</a></li>
</ul>
<hr />
<h2>Summary</h2>
<p>To prepare our dapps for social media sites and SEO we need:</p>
<ol>
<li>a title</li>
<li>a description or a catchy selling purpose</li>
<li>icons</li>
<li>a social image</li>
</ol>
<p>These static assets need to be served by our canister smart contracts and we have to set meta data in:</p>
<ul>
<li>all HTML pages of our application</li>
<li>a web app manifest</li>
<li>a sitemap.xml</li>
</ul>
<p>And we should not forget to allow crawlers to view our content by defining a robots.txt.</p>
<p>To infinity and beyond<br />
David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Compute sha256 of CSP <script/> in NodeJS]]></title>
          <description><![CDATA[Automatically generate the SHA-256 hash of the script tags for the Content Security Policy]]></description>
          <link>https://daviddalbusco.com/blog/compute-sha256-of-csp-script-in-nodejs/</link>
          <pubDate>Sat, 17 Sep 2022 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://images.unsplash.com/photo-1516750484197-6b28d10c91ea?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwzMDkyMzV8MHwxfHNlYXJjaHw0NXx8bWFueSUyMGNhdHN8ZW58MHx8fHwxNjYzNDIxNDgx&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=1080" alt="Delilah" / loading="lazy"></p>
<p><em>Kim Davies</em></p>
<p>I have been using the same strategy to compute automatically <code>SHA-256</code> hash for my <code>&lt;script /&gt;</code> tags since a couple of years and as I recently applied it again at work and in <a href="https://papy.rs" rel="noopener noreferrer external">Papyrs</a> (I am currently migrating the kit that generate the blogspaces from vanilla JavaScript to <a href="https://astro.build/" rel="noopener noreferrer external">Astro</a>), I thought it could be an interesting subject for a new short blog post.</p>
<hr />
<h2>Getting started</h2>
<p>While <code>sha256</code> can be set manually, I personally rather like to generate these automatically once the <code>build</code> script is over - as a post-processing job. This means that I rely on the fact that what is built is trustable and that I compute the values once the scripts have been generated.</p>
<p>To do so, I create a dedicated script - e.g. <code>./build-csp.mjs</code> - that I append to my build script:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">// package.json</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">scripts</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">build:csp</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">node build-csp.mjs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">build</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">astro build &#x26;&#x26; npm run build:csp</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h2>Search and update</h2>
<p>As I aim to inject the shas at the end of the process, I add a placeholder within the Content Security Policy (CSP) to search and update the computed values. e.g. in following CSP <code>meta</code> tag I used the placeholder <code>{{EXTRA\_SHAS}}</code>:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">meta</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    http-equiv</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Content-Security-Policy</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    content</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">default-src 'none';</span></span>
<span class="line"><span style="color:#F1FA8C">             script-src 'self' {{EXTRA_SHAS}};</span></span>
<span class="line"><span style="color:#F1FA8C">/></span></span></code></pre><p>Because such a placeholder is not a valid policy, the browser would throw an error while evaluating the rules. What can notably happen when I develop since I post process only the production build.</p>
<p>That is why I often just avoid the use of CSP for my local environment. e.g. in my Astro project I skip the rendering of the <code>meta</code> tag if not a <code>PROD</code> build.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">---</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> csp </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> import</span><span style="color:#F8F8F2">.meta.env.</span><span style="color:#BD93F9">PROD</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">---</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#F8F8F2">  csp </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">meta</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">      http-equiv</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Content-Security-Policy</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">      content</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">default-src 'none';</span></span>
<span class="line"><span style="color:#F1FA8C">      script-src 'self' {{EXTRA_SHAS}};</span></span>
<span class="line"><span style="color:#F1FA8C">    /></span></span>
<span class="line"><span style="color:#F1FA8C">  )</span></span>
<span class="line"><span style="color:#F1FA8C">}</span></span></code></pre><hr />
<h2>List all HTML files</h2>
<p>To compute the <code>sha256</code> sha for the content of the <code>&lt;script /&gt;</code> that are used in the HTML files I first need to, well, list the HTML files that have been built.</p>
<p>For such purpose, I use a recursive exploration function that I most probably found on Stackoverflow and keep using since then.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { readdirSync, lstatSync } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">fs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { join } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">path</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> findEntryPoints</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">dir</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">files</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    readdirSync</span><span style="color:#F8F8F2">(dir).</span><span style="color:#50FA7B">forEach</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">file</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> fullPath </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> join</span><span style="color:#F8F8F2">(dir, file);</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (</span><span style="color:#50FA7B">lstatSync</span><span style="color:#F8F8F2">(fullPath).</span><span style="color:#50FA7B">isDirectory</span><span style="color:#F8F8F2">()) {</span></span>
<span class="line"><span style="color:#50FA7B">            findEntryPoints</span><span style="color:#F8F8F2">(fullPath, files);</span></span>
<span class="line"><span style="color:#F8F8F2">        } </span><span style="color:#FF79C6">else</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">            files.</span><span style="color:#50FA7B">push</span><span style="color:#F8F8F2">(fullPath);</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> entryPoints </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> [];</span></span>
<span class="line"><span style="color:#50FA7B">findEntryPoints</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">dist</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, entryPoints);</span></span></code></pre><p>Once I got all the files, I filter those that are <code>.html</code>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { extname } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">path</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> htmlEntryPoints </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> entryPoints.</span><span style="color:#50FA7B">filter</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">entry</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">.html</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">].</span><span style="color:#50FA7B">includes</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">extname</span><span style="color:#F8F8F2">(entry)));</span></span></code></pre><hr />
<h2>Process and update CSP</h2>
<p>Once I got all the target files, I batch update these by reading their content, generating the related shas and ultimately update the rules.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { join } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">path</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { readFile } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">fs/promises</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> updateCSP</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">entry</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> indexHtml </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> readFile</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">join</span><span style="color:#F8F8F2">(process.</span><span style="color:#50FA7B">cwd</span><span style="color:#F8F8F2">(), entry), </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">utf-8</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> scriptHashes </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> computeHashes</span><span style="color:#F8F8F2">(indexHtml);</span></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#50FA7B"> writeCSP</span><span style="color:#F8F8F2">({ scriptHashes, indexHtml, entry });</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> promises </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> htmlEntryPoints.</span><span style="color:#50FA7B">map</span><span style="color:#F8F8F2">(updateCSP);</span></span>
<span class="line"><span style="color:#FF79C6">await</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">all</span><span style="color:#F8F8F2">(promises);</span></span></code></pre><hr />
<h2>Compute sha256</h2>
<p>To create the hashes for the content of the <code>&lt;script /&gt;</code> tags, I use a regex to find those tags and use the <a href="https://nodejs.org/api/crypto.html#cryptocreatehashalgorithm-options" rel="noopener noreferrer external">Crypto API</a> of NodeJS to effectively compute the values.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { createHash } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">crypto</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> computeHashes</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">indexHtml</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> sw </span><span style="color:#FF79C6">=</span><span style="color:#FF5555"> /</span><span style="color:#F1FA8C">&#x3C;script</span><span style="color:#8BE9FD">[</span><span style="color:#BD93F9">\s\S</span><span style="color:#8BE9FD">]</span><span style="color:#FF79C6">*?</span><span style="color:#F1FA8C">></span><span style="color:#FFB86C">(</span><span style="color:#8BE9FD">[</span><span style="color:#BD93F9">\s\S</span><span style="color:#8BE9FD">]</span><span style="color:#FF79C6">*?</span><span style="color:#FFB86C">)</span><span style="color:#F1FA8C">&#x3C;\/script></span><span style="color:#FF5555">/</span><span style="color:#FF79C6">gm</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> scriptHashes </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> [];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> m;</span></span>
<span class="line"><span style="color:#FF79C6">    while</span><span style="color:#F8F8F2"> ((m </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> sw.</span><span style="color:#50FA7B">exec</span><span style="color:#F8F8F2">(indexHtml))) {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> content </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> m[</span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        scriptHashes.</span><span style="color:#50FA7B">push</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`'sha256-</span><span style="color:#FF79C6">${</span><span style="color:#50FA7B">createHash</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">sha256</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">).</span><span style="color:#50FA7B">update</span><span style="color:#F8F8F2">(content).</span><span style="color:#50FA7B">digest</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">base64</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">'`</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> scriptHashes;</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><hr />
<h2>Write results</h2>
<p>Finally, I replace the placeholder I declared earlier with these effective shas.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { writeFile } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">fs/promises</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> writeCSP</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">scriptHashes</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">indexHtml</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">entry</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span></span>
<span class="line"><span style="color:#50FA7B">    writeFile</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#F8F8F2">        entry,</span></span>
<span class="line"><span style="color:#F8F8F2">        indexHtml.</span><span style="color:#50FA7B">replace</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">{{EXTRA_SHAS}}</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, scriptHashes.</span><span style="color:#50FA7B">map</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">sha256</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> sha256).</span><span style="color:#50FA7B">join</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#E9F284"> "</span><span style="color:#F8F8F2">)),</span></span>
<span class="line"><span style="color:#E9F284">        "</span><span style="color:#F1FA8C">utf-8</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span></code></pre><p>And voilà 🥳</p>
<hr />
<h2>Summary</h2>
<p>In life it is sometimes the small things that make you happy and developing this little helper script is one of those.</p>
<p>To infinity and beyond<br />
David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Having fun deconstructing the localstorage in TypeScript 🤙]]></title>
          <description><![CDATA[Read stringified objects from the localstorage in TypeScript using fun stuffs such as deconstructing objects, assertion and generic]]></description>
          <link>https://daviddalbusco.com/blog/having-fun-deconstructing-the-localstorage-in-typescript/</link>
          <pubDate>Thu, 01 Sep 2022 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://images.unsplash.com/photo-1528396518501-b53b655eb9b3?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwzMDkyMzV8MHwxfHNlYXJjaHw5N3x8c3RvcmFnZXxlbnwwfHx8fDE2NjIwMjQyOTQ&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=1080" alt="https://unsplash.com/photos/koyy-5uzlPU" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@katya?utm_source=Papyrs&amp;utm_medium=referral" rel="noopener noreferrer external">Katya Ross</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I recently implemented some features with the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage" rel="noopener noreferrer external">localstorage</a>. While I always had read values using the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Storage/getItem" rel="noopener noreferrer external">getItem()</a> method of the interface, I replaced this approach in my recent work with deconstruction of the storage object.</p>
<p>For no particular reason. I just like to deconstruct things, a lot 😄.</p>
<hr />
<h2>Old school</h2>
<p>Back in the days - until last few weeks 😉 - I would have probably implemented a function to read a stringified <code>object</code> from the storage as following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">type</span><span style="color:#8BE9FD;font-style:italic"> MyType</span><span style="color:#FF79C6"> =</span><span style="color:#8BE9FD;font-style:italic"> unknown</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> isValid</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#FFB86C;font-style:italic"> value</span><span style="color:#FF79C6"> is</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =></span><span style="color:#F8F8F2"> [</span><span style="color:#BD93F9">null</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">undefined</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">""</span><span style="color:#F8F8F2">].</span><span style="color:#50FA7B">includes</span><span style="color:#F8F8F2">(value);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> oldSchool</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> MyType</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> =></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> localStorage.</span><span style="color:#50FA7B">getItem</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">my_key</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#50FA7B">isValid</span><span style="color:#F8F8F2">(value)) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9"> JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">parse</span><span style="color:#F8F8F2">(value);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>i.e. I would have first get the <code>string</code> value (stringified <code>JSON.stringify()</code> representation of the object I would have saved in the storage) using <code>getItem()</code> before double checking its validity and parsing it back to an object.</p>
<hr />
<h2>New school</h2>
<p>While I nowadays keep following previous logic (&quot;read, check validity and parse&quot;), I am now deconstructing the storage to read the value.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> newSchool</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> MyType</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> =></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { my_key</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> value }</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Storage</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> localStorage;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#50FA7B">isValid</span><span style="color:#F8F8F2">(value)) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9"> JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">parse</span><span style="color:#F8F8F2">(value);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Again, no particular reason but, isn't it shinier? 👨‍🎨</p>
<p>This approach is possible in TypeScript because the <code>Storage</code> interface - representing the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API" rel="noopener noreferrer external">Storage API</a> - is actually declared as a map of keys of <code>any</code> types.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">interface</span><span style="color:#8BE9FD;font-style:italic"> Storage</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    readonly</span><span style="color:#F8F8F2"> length</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#50FA7B">    clear</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> void</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#50FA7B">    getItem</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">key</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#50FA7B">    key</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">index</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#50FA7B">    removeItem</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">key</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> void</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#50FA7B">    setItem</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">key</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> void</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#6272A4">    // HERE 😃 [name: string]: any;</span></span>
<span class="line"><span style="color:#F8F8F2">    [</span><span style="color:#FFB86C;font-style:italic">name</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">]</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> any</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h2>SSR &amp; pre-rendering</h2>
<p>The <code>localstorage</code> is a readonly property of the <code>window</code> interface - i.e. it exists only in the browser. To prevent my SvelteKit's static build to crash when I use it, I set an <code>undefined</code> fallback value for the NodeJS context.</p>
<p>Moreover, as in addition to the deconstruction pattern, I also like to inline everything (😄). So, I came up with the following code snippet to solve my inspiration:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { browser } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">$app/env</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> newSchool</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> MyType</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> =></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { my_key</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> value }</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Storage</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> browser</span></span>
<span class="line"><span style="color:#FF79C6">        ?</span><span style="color:#F8F8F2"> localStorage</span></span>
<span class="line"><span style="color:#FF79C6">        :</span><span style="color:#F8F8F2"> ({ my_key</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2"> } </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> unknown</span><span style="color:#FF79C6"> as</span><span style="color:#8BE9FD;font-style:italic"> Storage</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#50FA7B">isValid</span><span style="color:#F8F8F2">(value)) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9"> JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">parse</span><span style="color:#F8F8F2">(value);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><hr />
<h2>Generic</h2>
<p>At this point you might say &quot;Yes David, good, this is cool and stuffs but, what about reusability?&quot;. To which, I would answer &quot;Hold my beer, you can dynamically deconstruct objects&quot; 😉.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> newSchool</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">>(</span><span style="color:#FFB86C;font-style:italic">key</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> T</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> =></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { [key]</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> value }</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Storage</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> browser</span></span>
<span class="line"><span style="color:#FF79C6">        ?</span><span style="color:#F8F8F2"> localStorage</span></span>
<span class="line"><span style="color:#FF79C6">        :</span><span style="color:#F8F8F2"> ({ [key]</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2"> } </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> unknown</span><span style="color:#FF79C6"> as</span><span style="color:#8BE9FD;font-style:italic"> Storage</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#50FA7B">isValid</span><span style="color:#F8F8F2">(value)) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9"> JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">parse</span><span style="color:#F8F8F2">(value);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><hr />
<h2>Summary</h2>
<p>Returning <code>undefined</code> is convenient for demo purpose but, in actual implementations - such as the one I just unleashed this morning in <a href="https://papy.rs" rel="noopener noreferrer external">Papyrs</a> (a web3 blogging platform) - it might be useful to rather use default fallback values.</p>
<p>Therefore, here is the final form of my generic function to read items that have been saved in the <code>localstorage</code> in TypeScript using fun stuffs such as deconstructing objects, assertion and generic.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { browser } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">$app/env</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> isValid</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#FFB86C;font-style:italic"> value</span><span style="color:#FF79C6"> is</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =></span><span style="color:#F8F8F2"> [</span><span style="color:#BD93F9">null</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">undefined</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">""</span><span style="color:#F8F8F2">].</span><span style="color:#50FA7B">includes</span><span style="color:#F8F8F2">(value);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> getStorageItem</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">>({ </span><span style="color:#FFB86C;font-style:italic">key</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">defaultValue</span><span style="color:#F8F8F2"> }</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { key</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">; defaultValue</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> T</span><span style="color:#F8F8F2"> })</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> T</span><span style="color:#FF79C6"> =></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { [key]</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> value }</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Storage</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> browser</span></span>
<span class="line"><span style="color:#FF79C6">        ?</span><span style="color:#F8F8F2"> localStorage</span></span>
<span class="line"><span style="color:#FF79C6">        :</span><span style="color:#F8F8F2"> ({ [key]</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2"> } </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> unknown</span><span style="color:#FF79C6"> as</span><span style="color:#8BE9FD;font-style:italic"> Storage</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#50FA7B">isValid</span><span style="color:#F8F8F2">(value)) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> defaultValue;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9"> JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">parse</span><span style="color:#F8F8F2">(value);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>To infinity and beyond<br />
David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Install code in child canisters with NodeJS]]></title>
          <description><![CDATA[How to upgrade smart contracts on the Internet Computer with JavaScript]]></description>
          <link>https://daviddalbusco.com/blog/install-code-in-child-canisters-with-nodejs/</link>
          <pubDate>Thu, 11 Aug 2022 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://images.unsplash.com/photo-1620207418302-439b387441b0?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwzMDkyMzV8MHwxfHNlYXJjaHw1Nnx8YWJzdHJhY3R8ZW58MHx8fHwxNjU5MTgzOTI4&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=1080" alt="Fakurian.com" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@fakurian?utm_source=Papyrs&amp;utm_medium=referral" rel="noopener noreferrer external">Milad Fakurian</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<hr />
<p>On <a href="https://papy.rs" rel="noopener noreferrer external">Papyrs</a> - a web3 open source blogging platform - each user gets two smart contract canisters upon sign-in. One that contains private data and another that enables the user's personal blog-space on the internet.</p>
<p>Until the day I (hopefully) hand over the control of all canisters to a <a href="https://medium.com/dfinity/how-the-service-nervous-system-sns-will-bring-tokenized-governance-to-on-chain-dapps-b74fb8364a5c" rel="noopener noreferrer external">Sns</a> and the community, I might have to install new version of the code in users' smart contracts by my self - e.g. to fix issues (😅) or to deploy new features (😎).</p>
<p>This article describes how I can install code with <a href="https://nodejs.org/en/" rel="noopener noreferrer external">NodeJS</a> scripts and how you could do as well.</p>
<hr />
<h2>Getting started</h2>
<p>Earlier this year I published two related articles:</p>
<ol>
<li><p><a href="https://6zvwc-sqaaa-aaaal-aalma-cai.raw.ic0.app/d/dynamically-create-canister-smart-contracts-in-motoko" rel="noopener noreferrer external">Dynamically create smart contracts in Motoko</a></p></li>
<li><p><a href="https://6zvwc-sqaaa-aaaal-aalma-cai.raw.ic0.app/d/call-internet-computer-canister-smart-contracts-in-nodejs" rel="noopener noreferrer external">Call Internet Computer canisters in NodeJS</a></p></li>
</ol>
<p>These articles lead to this tutorial. The first display how to query canisters in NodeJS and the second how to create smart contracts on the fly - i.e. how to create canisters in which, I want to install newer version of my code 😜.</p>
<hr />
<h2>Child canister</h2>
<p>I use the first of the two above posts to create a local sample project. After compilation and deployment - to a local simulated IC network - I open my browser and create on the fly a child canister <code>renrk-eyaaa-aaaaa-aaada-cai</code>.</p>
<p><img src="https://6zvwc-sqaaa-aaaal-aalma-cai.raw.ic0.app/images/capture-d%E2%80%99e%CC%81cran-2022-08-06-a%CC%80-14.12.30.png?token=JeWh3mMWht9_YVR_UNkt1" alt="capture-d%E2%80%99e%CC%81cran-2022-08-06-a%CC%80-14.12.30.png" / loading="lazy"></p>
<p>This sample smart contract is the one I aim to update in following chapters. That is why I bump its version by modifying its source code as following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> Nat </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">mo:base/Nat</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">actor class Bucket(user: Text) = this {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    var version: Nat = 2; </span><span style="color:#6272A4">// &#x3C;-- Bump v2</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    public query func say() : async Text {</span></span>
<span class="line"><span style="color:#F8F8F2">      return </span><span style="color:#F1FA8C">"Hello World - "</span><span style="color:#F8F8F2"> # user # </span><span style="color:#F1FA8C">" - v"</span><span style="color:#F8F8F2"> # Nat.toText(version);</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Once modified, I have to re-generate the wasm binary that will be installed - deployed to the IC. To do so, I have to follow the workaround I shared in my previous post because, <a href="https://forum.dfinity.org/t/how-do-i-upgrade-child-canisters-that-were-dynamically-created-from-a-parent-canister-of-which-i-am-the-controlller-in-motoko/12289/27?u=peterparker" rel="noopener noreferrer external">currently</a>, there is &quot;no other way of producing the wasm of the imported class as a separate, non-embedded thing&quot;.</p>
<ol>
<li><p>Edit the configuration <code>dfx.json</code> to list the bucket actor.</p></li>
<li><p>Run the <code>dfx deploy</code> command to generate the files. The command will end in error (&quot;Error: Invalid data: Expected arguments but found none.&quot;) that can safely be ignored 😉.</p></li>
<li><p>Revert the change in <code>dfx.json</code>.</p></li>
</ol>
<hr />
<h2>Backend</h2>
<p>Only controllers of the canister can install new version of the code. As the child canisters are created by a main actor - which I named <code>manager</code> - I had to take care to add its principal to the list of controllers while updating the settings in my previous tutorials.</p>
<p>So in this solution, this actor will install the code and the NodeJS script will &quot;only&quot; be a caller.</p>
<p><img src="https://6zvwc-sqaaa-aaaal-aalma-cai.raw.ic0.app/images/excalidraw-1659866676272.webp?token=TdtbJr-ic5c_Q2V6JGQ-w" alt="excalidraw-1659866676272.webp" / loading="lazy"></p>
<p>The backend feature to install code - <code>install\_code</code> - is part of the <a href="https://github.com/dfinity/interface-spec/blob/master/spec/ic.did" rel="noopener noreferrer external">IC interface specification</a>. Therefore, I can add a function to my <code>manager</code> that acts as a proxy which receives the information and calls that core feature of the IC.</p>
<p><strong>Important note</strong>: following code snippet is a public function! If you implement such a feature in your smart contracts on <code>mainnet</code>, please apply the appropriate safety precautions.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> IC </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">./ic.types</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">actor Main {</span></span>
<span class="line"><span style="color:#F8F8F2">  private let ic : IC.Self = actor </span><span style="color:#F1FA8C">"aaaaa-aa"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  public func installCode(canisterId: Principal, arg: Blob, wasmModule: Blob): async() {</span></span>
<span class="line"><span style="color:#F8F8F2">      await ic.install_code({</span></span>
<span class="line"><span style="color:#F8F8F2">          arg = arg;</span></span>
<span class="line"><span style="color:#F8F8F2">          wasm_module = wasmModule;</span></span>
<span class="line"><span style="color:#F8F8F2">          mode = #upgrade;</span></span>
<span class="line"><span style="color:#F8F8F2">          canister_id = canisterId;</span></span>
<span class="line"><span style="color:#F8F8F2">      });</span></span>
<span class="line"><span style="color:#F8F8F2">  };</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>To install code in my target canister, I need four parameters:</p>
<ol>
<li>a target canister id</li>
<li>the wasm module - the new version of the wasm code I built in previous chapter with my workaround</li>
<li>a <code>mode</code> set to <code>#upgrade</code> to perform an update as described in <a href="https://internetcomputer.org/docs/current/references/ic-interface-spec/#system-api-upgrades" rel="noopener noreferrer external">Canister upgrades</a> - with the goal to maintain the state</li>
<li>arguments - those that are used to initialize the canister</li>
</ol>
<hr />
<h2>NodeJS script</h2>
<p>I can implement the call to the endpoint of the <code>manager</code> in a NodeJS module script I named <code>installcode.mjs</code>. The script will take care of collecting the parameters mentioned above before effectively calling my actor (function <code>upgradeBucket</code>).</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Principal } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@dfinity/principal</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { IDL } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@dfinity/candid</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> installCode</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#6272A4">    // Param 1.</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> canisterId </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> Principal.</span><span style="color:#50FA7B">fromText</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">renrk-eyaaa-aaaaa-aaada-cai</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // Param 2.</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> wasmModule </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> loadWasm</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // Param 3.</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> arg </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> IDL</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">encode</span><span style="color:#F8F8F2">([</span><span style="color:#BD93F9">IDL</span><span style="color:#F8F8F2">.Text], [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">User1</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">]);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // Agent-js actor</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> actor </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> managerActor</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // Execute</span></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#50FA7B"> upgradeBucket</span><span style="color:#F8F8F2">({ actor, wasmModule, canisterId, arg });</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#50FA7B"> installCode</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">} </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(err);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<p>The first parameter is the targeted canister id as <code>Principal</code>. As I collected the local child canister as a <code>string</code> when I printed its id - <code>renrk-eyaaa-aaaaa-aaada-cai</code> - in the browser console, I need to convert it the help of <code>Principal.fromText()</code>.</p>
<hr />
<p>The second parameter I need is the wasm module. To collect it, I can read the file that has been generated when I previously ran <code>dfx deploy</code> and can transform it to an <code>ArrayBuffer</code> - the expected type that matches to the <code>Blob</code> defined in the backend actor's code.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { readFileSync } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">fs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> loadWasm</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> localPath </span><span style="color:#FF79C6">=</span><span style="color:#F1FA8C"> `</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">process.</span><span style="color:#50FA7B">cwd</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">/.dfx/local/canisters/bucket/bucket.wasm`</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> buffer </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> readFileSync</span><span style="color:#F8F8F2">(localPath);</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> [</span><span style="color:#FF79C6">...</span><span style="color:#FF79C6;font-weight:bold">new</span><span style="color:#50FA7B"> Uint8Array</span><span style="color:#F8F8F2">(buffer)];</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><hr />
<p>The third parameter is the one that matches those use to create the canister on the fly 🤪. Concretely, the bucket's actors of this tutorial are created with a <code>user</code> parameter:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">actor </span><span style="color:#FF79C6">class</span><span style="color:#8BE9FD"> Bucket</span><span style="color:#F8F8F2">(</span><span style="color:#8BE9FD">user</span><span style="color:#F8F8F2">: </span><span style="color:#8BE9FD">Text</span><span style="color:#F8F8F2">) = </span><span style="color:#8BE9FD">this</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#6272A4">    // commented</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>So, to install the code, I need to provide the same parameters which has to be encoded with Candid (otherwise the parameters are rejected):</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { IDL } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@dfinity/candid</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> arg </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> IDL</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">encode</span><span style="color:#F8F8F2">([</span><span style="color:#BD93F9">IDL</span><span style="color:#F8F8F2">.Text], [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">User1</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">]);</span></span></code></pre><p>Note that <code>IDL</code> support various format - e.g. if the Motoko parameter would have been a <code>Principal</code>, I could have encoded it as following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { IDL } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@dfinity/candid</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Principal } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@dfinity/principal</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> arg </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> IDL</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">encode</span><span style="color:#F8F8F2">([</span><span style="color:#BD93F9">IDL</span><span style="color:#F8F8F2">.Principal], [Principal.</span><span style="color:#50FA7B">fromText</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">rrrrr-ccccc-user-principal</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">)]);</span></span></code></pre><hr />
<p>To instantiate the <code>manager</code> actor, once I find its canister ID, I can proceed as I would commonly do with <a href="https://github.com/dfinity/agent-js" rel="noopener noreferrer external">agent-js</a>:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { idlFactory } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./.dfx/local/canisters/manager/manager.did.mjs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> fetch </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">node-fetch</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { HttpAgent, Actor } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@dfinity/agent</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> managerActor</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> canisterId </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> managerPrincipalLocal</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // Replace host with https://ic0.app for mainnet</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> agent </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> HttpAgent</span><span style="color:#F8F8F2">({ fetch, host</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">http://localhost:8000/</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // Only if local IC</span></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#F8F8F2"> agent.</span><span style="color:#50FA7B">fetchRootKey</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> Actor.</span><span style="color:#50FA7B">createActor</span><span style="color:#F8F8F2">(idlFactory, {</span></span>
<span class="line"><span style="color:#F8F8F2">        agent,</span></span>
<span class="line"><span style="color:#F8F8F2">        canisterId</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>However, there is one subtlety: because I am writing a module script - <code>.mjs</code> - I cannot <code>import</code> the <code>idlFactory</code> script that was automatically generated by <code>dfx</code> as a <code>.js</code> file.</p>
<p>To overcome this issue, I just had to copy it to change its extension. Fortunately this does the trick.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">cp</span><span style="color:#F1FA8C"> ./.dfx/local/canisters/manager/manager.did.js</span><span style="color:#F1FA8C"> ./.dfx/local/canisters/manager/manager.did.mjs</span></span></code></pre><p>The principal ID of the <code>manager</code> deployed on a local simulated IC can be found in the <code>.dfx</code> folder.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> managerPrincipalLocal</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> buffer </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> readFileSync</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">./.dfx/local/canister_ids.json</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { manager } </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">parse</span><span style="color:#F8F8F2">(buffer.</span><span style="color:#50FA7B">toString</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">utf-8</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">));</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> Principal.</span><span style="color:#50FA7B">fromText</span><span style="color:#F8F8F2">(manager.local);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Ultimately, if you would deploy on <code>mainnet</code>, you would be able to find the same information in the <code>canister\_ids.json</code> present at the root of your project.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> managerPrincipalIC</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> buffer </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> readFileSync</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">./canister_ids.json</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { manager } </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">parse</span><span style="color:#F8F8F2">(buffer.</span><span style="color:#50FA7B">toString</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">utf-8</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">));</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> Principal.</span><span style="color:#50FA7B">fromText</span><span style="color:#F8F8F2">(manager.ic);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Note that you would also have to comment <code>fetchRootKey</code> and change the <code>host</code> property in the <code>HttpAgent</code> initialization.</p>
<hr />
<p>Finally, the effective call that will install the code can be implemented with the parameters I collected.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> upgradeBucket</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">actor</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">wasmModule</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">canisterId</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">arg</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`Upgrading: </span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">canisterId.</span><span style="color:#50FA7B">toText</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#F8F8F2"> actor.</span><span style="color:#50FA7B">installCode</span><span style="color:#F8F8F2">(canisterId, [</span><span style="color:#FF79C6">...</span><span style="color:#F8F8F2">arg], wasmModule);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`Done: </span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">canisterId.</span><span style="color:#50FA7B">toText</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><hr />
<h2>Test</h2>
<p>Everything is set. I can call my NodeJS script - <code>node installcode.mjs</code>.</p>
<p><img src="https://6zvwc-sqaaa-aaaal-aalma-cai.raw.ic0.app/images/capture-d%E2%80%99e%CC%81cran-2022-08-11-a%CC%80-07.28.30.png?token=uybSEtIIQCrd1yLDkqzwe" alt="capture-d%E2%80%99e%CC%81cran-2022-08-11-a%CC%80-07.28.30.png" / loading="lazy"></p>
<p>The installation was a success. To be certain the code was deployed, I called afterwards the canister - which was updated - to check that indeed, it now returned the new version - <code>v2</code> - I was expecting and, indeed it worked out 🎉.</p>
<hr />
<h2>Conclusion and sample repo</h2>
<p>You can find the source code of this tutorial in a sample repo I published on GitHub:</p>
<p>👉 <a href="https://github.com/peterpeterparker/manager" rel="noopener noreferrer external">https://github.com/peterpeterparker/manager</a></p>
<p>I hope it will be useful for the community and let me know if you have idea of improvements!</p>
<p>To infinity and beyond<br />
David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Parpaner Rothorn]]></title>
          <link>https://daviddalbusco.com/trails/2022/parpaner-rothorn/</link>
          <pubDate>Mon, 01 Aug 2022 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>Needless to say, these 2km+ were though for my skills. When I reached the summit I really needed a break and a fresh lemonade 😅.</p>
<p>But it was totally worth the effort. Going down was also a pleasant run, almost alone in the middle of the mountains and I spotted 9 marmots 😺.</p>
<hr />
<p>This trail itinerary is the FHGR Trail Run - T33 [1] provided by the canton Graubünden. There is a shorter version FHGR Trail Run - T26 [2] that spares 7km.</p>
<hr />
<p>Way points:</p>
<ul>
<li>Lantsch/Lenz, Biathlon Arena (1387m)</li>
<li>Bergrestaurant Scharmoin (7.9km, 1902m)</li>
<li>Hörnli Berg - Hörnlihütte Arosa (14.2km, 2493m)</li>
<li>Äplisee (16.8km, 2176m)</li>
<li>Panoramarestaurant Rothorngipfel (22km, 2856m)</li>
<li>Alp Sanaspan (26.6km, 2000m)</li>
<li>Lantsch/Lenz, Biathlon Arena (1387m)</li>
</ul>
<p>For the start, I recommend the Gpx coordinates. Until reaching Scharmoin, the path isn't that clear but afterwards it is quite easy to follow with yellow and white/red/white signs.</p>
<hr />
<p>[1] https://www.graubuenden.ch/fr/sentiers-itineraires/fhgr-trail-run-t33</p>
<p>[2] https://www.graubuenden.ch/fr/sentiers-itineraires/fhgr-trail-run-t26</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[ICP to cycles in JavaScript]]></title>
          <description><![CDATA[How to programmatically convert ICP to cycles in NodeJS?]]></description>
          <link>https://daviddalbusco.com/blog/icp-to-cycles-in-javascript/</link>
          <pubDate>Fri, 15 Jul 2022 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://images.unsplash.com/photo-1613905780946-26b73b6f6e11?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwzMDkyMzV8MHwxfHNlYXJjaHwyMHx8bWF0aHxlbnwwfHx8fDE2NTc4MDgxNTQ&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=1080" alt="https://unsplash.com/photos/4ApmfdVo32Q" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@michalmatlon?utm_source=Papyrs&amp;utm_medium=referral" rel="noopener noreferrer external">Michal Matlon</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>Until there is a library (e.g. <a href="https://github.com/dfinity/ic-js" rel="noopener noreferrer external">cmc-js</a> 👀) that facilitates the conversion of ICP to cycles in JavaScript, the following tutorial may help you implement such a transformation.</p>
<hr />
<h2>Introduction</h2>
<p>Long story short, to convert ICP to cycles you need two things:</p>
<ol>
<li>the exchange rate to transform the values which is returned by the cycles-minting canister (CMC) through the function <code>get_icp_xdr_conversion_rate</code></li>
<li>a function that, well, does the conversion</li>
</ol>
<hr />
<h2>Dependencies</h2>
<p>To query the CMC in JavaScript easily you need <a href="https://github.com/dfinity/agent-js" rel="noopener noreferrer external">agent-js</a> (and its peer dependencies). In addition, because following solution runs in a NodeJS context and not in a browser, <a href="https://github.com/node-fetch/node-fetch" rel="noopener noreferrer external">node-fetch</a> is required as well to provide previous library a way to perform <code>XMLHttpRequest</code>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> i</span><span style="color:#F1FA8C"> node-fetch</span><span style="color:#F1FA8C"> @dfinity/agent</span><span style="color:#F1FA8C"> @dfinity/candid</span><span style="color:#F1FA8C"> @dfinity/principal</span></span></code></pre><hr />
<h2>Candid</h2>
<p>The description of the interface of the cycles-minting canister has to be used to initialize the communication channel.</p>
<p>The CMC candid file can be downloaded on the Internet Computer <a href="https://github.com/dfinity/ic/tree/master/rs/nns/cmc" rel="noopener noreferrer external">repo</a>. Its binding files ( <code>.idl.js</code>, <code>.d.ts</code> etc. ) can be generated with the <a href="https://github.com/dfinity/candid/tree/master/tools/didc" rel="noopener noreferrer external">didc</a> command line tool or <a href="https://k7gat-daaaa-aaaae-qaahq-cai.raw.ic0.app/docs/" rel="noopener noreferrer external">Canlista</a> (a GUI version of the tool) but, if you want to spare yourself such operations, you can download all these files directly from the backend <a href="https://github.com/papyrs/ic/tree/main/ic/cmc" rel="noopener noreferrer external">repo</a> of my project <a href="https://papy.rs" rel="noopener noreferrer external">Papyrs</a>.</p>
<p>In following code snippets I use these particular bindings.</p>
<hr />
<h2>XDR conversion rate</h2>
<p>On mainnet, the canister ID of the CMC is <code>rkp4c-7iaaa-aaaaa-aaaca-cai</code>. This ID should be used to instantiate the actor in order to query <code>get_icp_xdr_conversion_rate</code> which returns <code>xdr_permyriad_per_icp</code>, the actual conversion rate in XDR we are looking for.</p>
<p>1 XDR being equal to 1 Trillion cycles, I convert the result to trillion ratio before returning it.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> pkgAgent </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@dfinity/agent</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> fetch </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">node-fetch</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { idlFactory } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./cmc/cmc.utils.did.mjs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> { HttpAgent, Actor } </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> pkgAgent;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> icpXdrConversionRate</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> agent </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> HttpAgent</span><span style="color:#F8F8F2">({ fetch, host</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">https://ic0.app</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> actor </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> Actor.</span><span style="color:#50FA7B">createActor</span><span style="color:#F8F8F2">(idlFactory, {</span></span>
<span class="line"><span style="color:#F8F8F2">        agent,</span></span>
<span class="line"><span style="color:#F8F8F2">        canisterId</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">rkp4c-7iaaa-aaaaa-aaaca-cai</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        data</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { xdr_permyriad_per_icp }</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> actor.</span><span style="color:#50FA7B">get_icp_xdr_conversion_rate</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> CYCLES_PER_XDR </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> BigInt</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">1_000_000_000_000</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // Return conversion rate in trillion ratio</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> (xdr_permyriad_per_icp </span><span style="color:#FF79C6">*</span><span style="color:#F8F8F2"> CYCLES_PER_XDR) </span><span style="color:#FF79C6">/</span><span style="color:#50FA7B"> BigInt</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">10_000</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><hr />
<h2>Conversion ICP to cycles</h2>
<p>We aim to convert readable numbers. That is why, we first need a small utility that maps <code>number</code> to <code>BigInt</code> (because the conversion function will process such types).</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> E8S_PER_ICP </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> BigInt</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">100000000</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> toBigint</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">amount</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> [integral, fractional] </span><span style="color:#FF79C6">=</span><span style="color:#F1FA8C"> `</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">amount</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">split</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">.</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> ((fractional </span><span style="color:#FF79C6">??</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">0</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">).length </span><span style="color:#FF79C6">></span><span style="color:#BD93F9"> 8</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        throw</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Error</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">More than 8 decimals not supported.</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#50FA7B"> BigInt</span><span style="color:#F8F8F2">(integral </span><span style="color:#FF79C6">??</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">*</span><span style="color:#F8F8F2"> E8S_PER_ICP </span><span style="color:#FF79C6">+</span><span style="color:#50FA7B"> BigInt</span><span style="color:#F8F8F2">((fractional </span><span style="color:#FF79C6">??</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">0</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">).</span><span style="color:#50FA7B">padEnd</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">8</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">0</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">));</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>A few samples of above function result (number -&gt; bigint):</p>
<ul>
<li><code>1</code> -&gt; <code>100000000n</code></li>
<li><code>1.2</code> -&gt; <code>120000000n</code></li>
<li><code>34.456</code> -&gt; <code>3445600000n</code></li>
</ul>
<hr />
<p>Finally, to effectively convert the ICP to cycles, we can implement a cross-multiplication with the conversion rate.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> icpToCycles</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">icp</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">conversionRate</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> e8ToCycleRatio </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> conversionRate </span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2"> E8S_PER_ICP;</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#50FA7B"> toBigint</span><span style="color:#F8F8F2">(icp) </span><span style="color:#FF79C6">*</span><span style="color:#F8F8F2"> e8ToCycleRatio;</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><hr />
<h2>Demo</h2>
<p>Put together in a small demo, e.g. above functions can be chained to log the result of the conversion to the console.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> convertICPToCycles</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">icp</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> conversionRate </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> icpXdrConversionRate</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> cycles </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> icpToCycles</span><span style="color:#F8F8F2">({ icp, conversionRate });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> ONE_TRILLION </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> BigInt</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">1000000</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">*</span><span style="color:#50FA7B"> BigInt</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">1000000</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">icp</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C"> ICP equals </span><span style="color:#FF79C6">${</span><span style="color:#50FA7B">Number</span><span style="color:#F8F8F2">(cycles)</span><span style="color:#FF79C6"> /</span><span style="color:#50FA7B"> Number</span><span style="color:#F8F8F2">(ONE_TRILLION)</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C"> (</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">cycles</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">) cycles`</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">await</span><span style="color:#50FA7B"> convertICPToCycles</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">123.56</span><span style="color:#F8F8F2">);</span></span></code></pre><p>If run in a command line, the sample should output such a result:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">❯</span><span style="color:#F1FA8C"> node</span><span style="color:#F1FA8C"> index.mjs</span></span>
<span class="line"><span style="color:#50FA7B">123.56</span><span style="color:#F1FA8C"> ICP</span><span style="color:#F1FA8C"> equals</span><span style="color:#BD93F9"> 652.0879</span><span style="color:#F8F8F2"> (652087900000000) cycles</span></span></code></pre><hr />
<h2>Conclusion</h2>
<p>I hope this small tutorial will be useful. If you have any idea of improvements, let me know!</p>
<p>To infinity and beyond<br />
David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[SvelteKit Web Worker]]></title>
          <description><![CDATA[How to create and communicate with a web worker in SvelteKit and ViteJS]]></description>
          <link>https://daviddalbusco.com/blog/sveltekit-web-worker/</link>
          <pubDate>Thu, 07 Jul 2022 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://images.unsplash.com/photo-1504917595217-d4dc5ebe6122?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwzMDkyMzV8MHwxfHNlYXJjaHw1fHxjb25zdHJ1Y3Rpb258ZW58MHx8fHwxNjU2ODcwOTM0&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=1080" alt="In the line of fire" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@christopher__burns?utm_source=Papyrs&amp;utm_medium=referral" rel="noopener noreferrer external">Christopher Burns</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I ❤️ <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers" rel="noopener noreferrer external">web workers</a>!</p>
<p>These background threads are my go to strategy to compute anything I consider as too expensive for a frontend web application (e.g. <a href="https://tietracker.com" rel="noopener noreferrer external">Tie Tracker</a>) or if it has to execute a recurring tasks (e.g. <a href="https://papy.rs/" rel="noopener noreferrer external">Papyrs</a> or <a href="https://cycles.watch/" rel="noopener noreferrer external">Cycles.watch</a>).</p>
<p>In these two last projects, I used <a href="https://kit.svelte.dev/" rel="noopener noreferrer external">SvelteKit</a>. So here is how you can develop and communicate with web workers using such an application framework.</p>
<hr />
<h2>Create a web worker</h2>
<p>There is no particular requirements nor convention but I like to suffix my worker files with the extension <code>.worker.ts</code> - e.g. to create a bare minimum worker I create a file named <code>my.worker.ts</code> which finds place in the <code>src/lib</code> directory.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">onmessage</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Hello World 👋</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#F8F8F2"> {};</span></span></code></pre><p><a href="https://developer.mozilla.org/en-US/docs/Web/API/Worker/message_event" rel="noopener noreferrer external">onmessage</a> is a function that fires each time the web worker gets called - i.e. each time <a href="https://developer.mozilla.org/en-US/docs/Web/API/Worker/postMessage" rel="noopener noreferrer external">postMessage</a> is used to send a message to the worker from the window side.</p>
<p><img src="https://6zvwc-sqaaa-aaaal-aalma-cai.raw.ic0.app/images/excalidraw-1657189373053.webp?token=jFxecJ-Kl7eZ35n00rXn7" alt="excalidraw-1657189373053.webp" / loading="lazy"></p>
<p>The empty <code>export {}</code> is a handy way to make the worker a module and solve following TypeScript error if not declared:</p>
<p><em>TS1208: 'my.worker.ts' cannot be compiled under '--isolatedModules' because it is considered a global script file. Add an import, export, or an empty 'export {}' statement to make it a module.</em></p>
<hr />
<h2>Dynamically import</h2>
<p>To integrate the worker in a component it needs to be dynamically imported. SvelteKit relying on <a href="https://vitejs.dev/" rel="noopener noreferrer external">ViteJS</a> for its tooling, it can be imported with the <code>?worker</code> suffix (see <a href="https://vitejs.dev/guide/assets.html#importing-script-as-a-worker" rel="noopener noreferrer external">documentation</a>).</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2">script lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ts</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">></span></span>
<span class="line"><span style="color:#FF79C6">  import</span><span style="color:#F8F8F2"> { onMount } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">svelte</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  let</span><span style="color:#F8F8F2"> syncWorker</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Worker</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> =</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#50FA7B"> loadWorker</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> SyncWorker </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#FF79C6"> import</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">$lib/my.worker?worker</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    syncWorker </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#F8F8F2"> SyncWorker.</span><span style="color:#50FA7B">default</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  onMount</span><span style="color:#F8F8F2">(loadWorker);</span></span>
<span class="line"><span style="color:#FF79C6">&#x3C;/</span><span style="color:#F8F8F2">script</span><span style="color:#FF79C6">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">h1</span><span style="color:#F8F8F2">>Web worker demo</span><span style="color:#FF79C6">&#x3C;/</span><span style="color:#F8F8F2">h1</span><span style="color:#FF79C6">></span></span></code></pre><p>In above snippet I affect the worker to a local variable in case I would like to use it elsewhere in the component. Notably on <code>onDestroy</code> callback if I would clean up or propagate the destruction to the worker.</p>
<hr />
<h2>PostMessage: window -&gt; web worker</h2>
<p>To send a message from the window side to the web worker we need a reference to the loaded module. For such purpose we can use the variable I declared in previous chapter. e.g. we can send an empty message - empty object - right after the initialization.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> loadWorker</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> SyncWorker </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#FF79C6"> import</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">$lib/my.worker?worker</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    syncWorker </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#F8F8F2"> SyncWorker.</span><span style="color:#50FA7B">default</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    syncWorker.</span><span style="color:#50FA7B">postMessage</span><span style="color:#F8F8F2">({});</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Assuming everything goes according plan, the web worker should receive the message and log to the console the &quot;Hello World 👋&quot;.</p>
<p><img src="https://6zvwc-sqaaa-aaaal-aalma-cai.raw.ic0.app/images/capture-d%E2%80%99e%CC%81cran-2022-07-07-a%CC%80-13.00.02.png?token=543zp2yH5rX5JO9Pr4rD1" alt="capture-d%E2%80%99e%CC%81cran-2022-07-07-a%CC%80-13.00.02.png" / loading="lazy"></p>
<p><em>If you face the issue &quot;SyntaxError: import declarations may only appear at top level of a module&quot; while following this article, note that the use of web workers in development is only supported in selected browsers (see issue <a href="https://github.com/vitejs/vite/issues/4586" rel="noopener noreferrer external">#4586</a> in ViteJS).</em></p>
<hr />
<h2>PostMessage: web worker -&gt; window</h2>
<p>Messages are bidirectional - i.e. web worker can also send messages to the window side. To do so, <code>postMessage</code> is used as well. The only difference with previous transmission channel is that it does not need an object or module as reference to trigger the message.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">onmessage</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Hello World 👋</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    postMessage</span><span style="color:#F8F8F2">({});</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#F8F8F2"> {};</span></span></code></pre><p>In above snippet I send an empty message object from the web worker to the window each time the worker receives a messages (#yolo).</p>
<p>To intercept these messages on the window side, a function that fires every time such messages are send need to be registered. The loaded worker exposes an <code>onmessage</code> property for such purpose.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2">script lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ts</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">></span></span>
<span class="line"><span style="color:#FF79C6">  import</span><span style="color:#F8F8F2"> { onMount } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">svelte</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  let</span><span style="color:#F8F8F2"> syncWorker</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Worker</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> =</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#50FA7B"> onWorkerMessage</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">      console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Cool it works out 😃</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">  };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#50FA7B"> loadWorker</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> SyncWorker </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#FF79C6"> import</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">$lib/my.worker?worker</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    syncWorker </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#F8F8F2"> SyncWorker.</span><span style="color:#50FA7B">default</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    syncWorker.onmessage </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> onWorkerMessage;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    syncWorker.</span><span style="color:#50FA7B">postMessage</span><span style="color:#F8F8F2">({});</span></span>
<span class="line"><span style="color:#F8F8F2">  };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  onMount</span><span style="color:#F8F8F2">(loadWorker);</span></span>
<span class="line"><span style="color:#FF79C6">&#x3C;/</span><span style="color:#F8F8F2">script</span><span style="color:#FF79C6">></span></span></code></pre><p>If I refresh my test browser and console, previous message &quot;Hello World 👋&quot; is still printed but, in addition, the new message &quot;Cool it works out 😃&quot; is added to the console as well.</p>
<p><img src="https://6zvwc-sqaaa-aaaal-aalma-cai.raw.ic0.app/images/capture-d%E2%80%99e%CC%81cran-2022-07-07-a%CC%80-13.08.51.png?token=pD5YWfMeq6HE4w7BttQs9" alt="capture-d%E2%80%99e%CC%81cran-2022-07-07-a%CC%80-13.08.51.png" / loading="lazy"></p>
<hr />
<h2>TypeScript</h2>
<p>Sending empty messages is cute for demo purpose but you might want to define some TypeScript definition to improve the code.</p>
<p>What I generally do is defining some types for the requests and responses that are identified with a typed identificator and contains information next to them. e.g. I often use a variable named <code>msg</code> and a <code>data</code> object that effectively contains the information.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> interface</span><span style="color:#8BE9FD;font-style:italic"> PostMessageDataRequest</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    text</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> interface</span><span style="color:#8BE9FD;font-style:italic"> PostMessageDataResponse</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    text</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> type</span><span style="color:#8BE9FD;font-style:italic"> PostMessageRequest</span><span style="color:#FF79C6"> =</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">request1</span><span style="color:#E9F284">"</span><span style="color:#FF79C6"> |</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">start</span><span style="color:#E9F284">"</span><span style="color:#FF79C6"> |</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">stop</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> type</span><span style="color:#8BE9FD;font-style:italic"> PostMessageResponse</span><span style="color:#FF79C6"> =</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">response1</span><span style="color:#E9F284">"</span><span style="color:#FF79C6"> |</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">response2</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> interface</span><span style="color:#8BE9FD;font-style:italic"> PostMessage</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#FF79C6"> extends</span><span style="color:#FFB86C;font-style:italic"> PostMessageDataRequest</span><span style="color:#FF79C6"> |</span><span style="color:#FFB86C;font-style:italic"> PostMessageDataResponse</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#F8F8F2">    msg</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> PostMessageRequest</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> PostMessageResponse</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    data</span><span style="color:#FF79C6">?:</span><span style="color:#8BE9FD;font-style:italic"> T</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Again here, no particular reason or best practice, just a thing I do to clean up my code 😄.</p>
<p>Thanks to these definitions, I can now set types for the <code>onmessage</code> function of the web worker.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#FF79C6"> type</span><span style="color:#F8F8F2"> { PostMessage, PostMessageDataRequest } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./post-message</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">onmessage</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ({ data: { </span><span style="color:#FFB86C;font-style:italic">data</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">msg</span><span style="color:#F8F8F2"> } }</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> MessageEvent</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">PostMessage</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">PostMessageDataRequest</span><span style="color:#F8F8F2">>>) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(msg, data);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> message</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> PostMessage</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">PostMessageDataRequest</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        msg</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">response1</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        data</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { text</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Cool it works out v2 🥳</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> }</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"><span style="color:#50FA7B">    postMessage</span><span style="color:#F8F8F2">(message);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#F8F8F2"> {};</span></span></code></pre><p>Likewise, types can be defined in the component.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2">script lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ts</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">></span></span>
<span class="line"><span style="color:#FF79C6">  import</span><span style="color:#F8F8F2"> { onMount } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">svelte</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">  import</span><span style="color:#FF79C6"> type</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    PostMessage,</span></span>
<span class="line"><span style="color:#F8F8F2">    PostMessageDataRequest,</span></span>
<span class="line"><span style="color:#F8F8F2">    PostMessageDataResponse</span></span>
<span class="line"><span style="color:#F8F8F2">  } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">../lib/post-message</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  let</span><span style="color:#F8F8F2"> syncWorker</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Worker</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> =</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#50FA7B"> onWorkerMessage</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ({</span></span>
<span class="line"><span style="color:#F8F8F2">    data</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { msg, data }</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> MessageEvent</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">PostMessage</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">PostMessageDataResponse</span><span style="color:#F8F8F2">>>) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(msg, data);</span></span>
<span class="line"><span style="color:#F8F8F2">  };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#50FA7B"> loadWorker</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> SyncWorker </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#FF79C6"> import</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">$lib/my.worker?worker</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    syncWorker </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#F8F8F2"> SyncWorker.</span><span style="color:#50FA7B">default</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    syncWorker.onmessage </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> onWorkerMessage;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> message</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> PostMessage</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">PostMessageDataRequest</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">      msg</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">request1</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">      data</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { text</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">Hello World v2 🤪</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2"> }</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"><span style="color:#F8F8F2">    syncWorker.</span><span style="color:#50FA7B">postMessage</span><span style="color:#F8F8F2">(message);</span></span>
<span class="line"><span style="color:#F8F8F2">  };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  onMount</span><span style="color:#F8F8F2">(loadWorker);</span></span>
<span class="line"><span style="color:#FF79C6">&#x3C;/</span><span style="color:#F8F8F2">script</span><span style="color:#FF79C6">></span></span></code></pre><p>As I now use objects and modified the information that are transmitted, the messages printed in the console of my browser now reflects these changes.</p>
<p><img src="https://6zvwc-sqaaa-aaaal-aalma-cai.raw.ic0.app/images/capture-d%E2%80%99e%CC%81cran-2022-07-07-a%CC%80-13.35.26.png?token=3DZJGZAuyYVLopkbbx8zn" alt="capture-d%E2%80%99e%CC%81cran-2022-07-07-a%CC%80-13.35.26.png" / loading="lazy"></p>
<hr />
<h2>Cronjob</h2>
<p>As mentioned in the introduction, I use web worker to execute recurring tasks - to schedule cronjob. e.g in <a href="https//papyrs.rs" rel="noopener noreferrer external">Papyrs</a>, I use web worker jobs to synchronize the content of the blog posts that are edited and saved on the client side with the <a href="https://internetcomputer.org/" rel="noopener noreferrer external">Internet Computer</a>. Thanks to this approach, the user gets a smooth experience as the UI is not affected by any network communication.</p>
<p>When I implement such a timer in a web worker, I always define &quot;start&quot; and &quot;stop&quot; functions. The first being called when the web worker is loaded on the window side and the second being called when the related component that uses it gets destroyed.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#FF79C6"> type</span><span style="color:#F8F8F2"> { PostMessage, PostMessageDataRequest } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./post-message</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">onmessage</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ({ data: { </span><span style="color:#FFB86C;font-style:italic">msg</span><span style="color:#F8F8F2"> } }</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> MessageEvent</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">PostMessage</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">PostMessageDataRequest</span><span style="color:#F8F8F2">>>) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    switch</span><span style="color:#F8F8F2"> (msg) {</span></span>
<span class="line"><span style="color:#FF79C6">        case</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">start</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">:</span></span>
<span class="line"><span style="color:#50FA7B">            startTimer</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">            break</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">        case</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">stop</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">:</span></span>
<span class="line"><span style="color:#50FA7B">            stopTimer</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">let</span><span style="color:#F8F8F2"> timer</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> NodeJS</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">Timeout</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> =</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> print</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`Timer </span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">performance.</span><span style="color:#50FA7B">now</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">ms ⏱`</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> startTimer</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> (timer </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> setInterval</span><span style="color:#F8F8F2">(print, </span><span style="color:#BD93F9">1000</span><span style="color:#F8F8F2">));</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> stopTimer</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">timer) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    clearInterval</span><span style="color:#F8F8F2">(timer);</span></span>
<span class="line"><span style="color:#F8F8F2">    timer </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#F8F8F2"> {};</span></span></code></pre><p>To schedule the time I use <a href="https://developer.mozilla.org/en-US/docs/Web/API/setInterval" rel="noopener noreferrer external">setInterval</a> which repeatedly calls a function or executes a code snippet, with a fixed time delay between each call. In above example, it log to the console every second.</p>
<p>On the window side, I use <code>postMessage</code> again to trigger the two events.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2">script lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ts</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">></span></span>
<span class="line"><span style="color:#FF79C6">  import</span><span style="color:#F8F8F2"> { onDestroy, onMount } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">svelte</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  let</span><span style="color:#F8F8F2"> syncWorker</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Worker</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> =</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#50FA7B"> loadWorker</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> SyncWorker </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#FF79C6"> import</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">$lib/my.worker?worker</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    syncWorker </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#F8F8F2"> SyncWorker.</span><span style="color:#50FA7B">default</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    syncWorker.</span><span style="color:#50FA7B">postMessage</span><span style="color:#F8F8F2">({ msg</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">start</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2"> });</span></span>
<span class="line"><span style="color:#F8F8F2">  };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  onMount</span><span style="color:#F8F8F2">(loadWorker);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  onDestroy</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> syncWorker?.</span><span style="color:#50FA7B">postMessage</span><span style="color:#F8F8F2">({ msg</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">stop</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2"> }));</span></span>
<span class="line"><span style="color:#FF79C6">&#x3C;/</span><span style="color:#F8F8F2">script</span><span style="color:#FF79C6">></span></span></code></pre><p>After updating my browser one last time, I find out <code>console.log</code> that are effectively printed according the interval I defined in my web worker.</p>
<p><img src="https://6zvwc-sqaaa-aaaal-aalma-cai.raw.ic0.app/images/capture-d%E2%80%99e%CC%81cran-2022-07-07-a%CC%80-13.46.53.png?token=zYQauEtmynxix3lqVml0C" alt="capture-d%E2%80%99e%CC%81cran-2022-07-07-a%CC%80-13.46.53.png" / loading="lazy"></p>
<hr />
<h2>Conclusion</h2>
<p>Web worker are pure coding joy to me 🤓.</p>
<p>To infinity and beyond<br />
David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Build a library with esbuild (vol. 2)]]></title>
          <description><![CDATA[A few new tips and tricks to build a library with esbuild]]></description>
          <link>https://daviddalbusco.com/blog/build-a-library-with-esbuild-vol-2/</link>
          <pubDate>Tue, 05 Jul 2022 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://images.unsplash.com/photo-1502933691298-84fc14542831?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwzMDkyMzV8MHwxfHNlYXJjaHwyOHx8c3VyZnxlbnwwfHx8fDE2NTcwMzY1NjI&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=1080" alt="Wake Skate" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@lime517?utm_source=Papyrs&amp;utm_medium=referral" rel="noopener noreferrer external">Joseph Greve</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<hr />
<p>A year ago I shared a <a href="https://daviddalbusco.com/blog/build-a-library-with-esbuild" rel="noopener noreferrer external">post</a> that explains how to build a library with <a href="https://esbuild.github.io/" rel="noopener noreferrer external">esbuild</a>. While it remains a valid solution, I developed some improvements in my tooling since its publication.</p>
<p>Here are these few add-ons that I hope, will be useful for your projects too.</p>
<hr />
<h2>Source and output</h2>
<p>It can be sometimes useful to define more than one entry point for the library - i.e. not just use a unique <code>index.ts</code> file as entry but multiple sources that provides logically-independent groups of code. esbuild supports such option through the parameter <a href="https://esbuild.github.io/api/#entry-points" rel="noopener noreferrer external">entryPoints</a>.</p>
<p>For example, in my projects, I often list all the TypeScript files present in my <code>src</code> folder and use these as separate entries.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { readdirSync, statSync } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">fs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { join } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">path</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// Select all typescript files of src directory as entry points</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> entryPoints </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> readdirSync</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">join</span><span style="color:#F8F8F2">(process.</span><span style="color:#50FA7B">cwd</span><span style="color:#F8F8F2">(), </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">src</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">))</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">filter</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">file</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> file.</span><span style="color:#50FA7B">endsWith</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">.ts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#50FA7B"> statSync</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">join</span><span style="color:#F8F8F2">(process.</span><span style="color:#50FA7B">cwd</span><span style="color:#F8F8F2">(), </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">src</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, file)).</span><span style="color:#50FA7B">isFile</span><span style="color:#F8F8F2">())</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">map</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">file</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F1FA8C"> `src/</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">file</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">);</span></span></code></pre><p>As the output folder before each build might have been deleted, I also like to ensure it exists by creating it before proceeding.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { existsSync, mkdirSync } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">fs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { join } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">path</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// Create dist before build if not exist</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> dist </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> join</span><span style="color:#F8F8F2">(process.</span><span style="color:#50FA7B">cwd</span><span style="color:#F8F8F2">(), </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">dist</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#50FA7B">existsSync</span><span style="color:#F8F8F2">(dist)) {</span></span>
<span class="line"><span style="color:#50FA7B">    mkdirSync</span><span style="color:#F8F8F2">(dist);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// Select entryPoints and build</span></span></code></pre><hr />
<h2>Global is not defined</h2>
<p>Your library might use some dependency that leads to a build error <em>&quot;Uncaught ReferenceError: global is not defined&quot;</em> when building ESM target. Root cause being the dependency expecting a <code>global</code> object (as in NodeJS) while you would need <code>window</code> for the browser.</p>
<p>To overcome the issue, esbuild has a <a href="https://esbuild.github.io/api/#define" rel="noopener noreferrer external">define</a> option that can be use to replace global identifiers with constant expression.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> esbuild </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">esbuild</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">esbuild</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">build</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        entryPoints,</span></span>
<span class="line"><span style="color:#F8F8F2">        outdir</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">dist/esm</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        bundle</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        sourcemap</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        minify</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        splitting</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        format</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">esm</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        define</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { global</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">window</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> },</span></span>
<span class="line"><span style="color:#F8F8F2">        target</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">esnext</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">]</span></span>
<span class="line"><span style="color:#F8F8F2">    })</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">catch</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> process.</span><span style="color:#50FA7B">exit</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">));</span></span></code></pre><hr />
<h2>Both esm and cjs</h2>
<p>To ship a library that supports both CommonJS (cjs) and ECMAScript module (esm), I output the bundles in two sub-folders of the distribution directory - e.g. <code>dist/cjs</code> and <code>dist/esm</code>. With esbuild, this can be achieved by specifying the options <a href="https://esbuild.github.io/api/#outdir" rel="noopener noreferrer external">outdir</a> or <a href="https://esbuild.github.io/api/#outfile" rel="noopener noreferrer external">outfile</a> to these relative paths.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> esbuild </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">esbuild</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { existsSync, mkdirSync, readdirSync, statSync, writeFileSync } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">fs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { join } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">path</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> dist </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> join</span><span style="color:#F8F8F2">(process.</span><span style="color:#50FA7B">cwd</span><span style="color:#F8F8F2">(), </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">dist</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#50FA7B">existsSync</span><span style="color:#F8F8F2">(dist)) {</span></span>
<span class="line"><span style="color:#50FA7B">    mkdirSync</span><span style="color:#F8F8F2">(dist);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> entryPoints </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> readdirSync</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">join</span><span style="color:#F8F8F2">(process.</span><span style="color:#50FA7B">cwd</span><span style="color:#F8F8F2">(), </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">src</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">))</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">filter</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">file</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#FF79C6"> !</span><span style="color:#F8F8F2">file.</span><span style="color:#50FA7B">endsWith</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">.ts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#50FA7B"> statSync</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">join</span><span style="color:#F8F8F2">(process.</span><span style="color:#50FA7B">cwd</span><span style="color:#F8F8F2">(), </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">src</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, file)).</span><span style="color:#50FA7B">isFile</span><span style="color:#F8F8F2">())</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">map</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">file</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F1FA8C"> `src/</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">file</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// esm output bundles with code splitting</span></span>
<span class="line"><span style="color:#F8F8F2">esbuild</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">build</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        entryPoints,</span></span>
<span class="line"><span style="color:#F8F8F2">        outdir</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">dist/esm</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        bundle</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        sourcemap</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        minify</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        splitting</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        format</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">esm</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        define</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { global</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">window</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> },</span></span>
<span class="line"><span style="color:#F8F8F2">        target</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">esnext</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">]</span></span>
<span class="line"><span style="color:#F8F8F2">    })</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">catch</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> process.</span><span style="color:#50FA7B">exit</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">));</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// cjs output bundle</span></span>
<span class="line"><span style="color:#F8F8F2">esbuild</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">build</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        entryPoints</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">src/index.ts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">        outfile</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">dist/cjs/index.cjs.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        bundle</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        sourcemap</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        minify</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        platform</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">node</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        target</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">node16</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">]</span></span>
<span class="line"><span style="color:#F8F8F2">    })</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">catch</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> process.</span><span style="color:#50FA7B">exit</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">));</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// an entry file for cjs at the root of the bundle</span></span>
<span class="line"><span style="color:#50FA7B">writeFileSync</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">join</span><span style="color:#F8F8F2">(dist, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">index.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">), </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">export * from './esm/index.js';</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// an entry file for esm at the root of the bundle</span></span>
<span class="line"><span style="color:#50FA7B">writeFileSync</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">join</span><span style="color:#F8F8F2">(dist, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">index.cjs.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">), </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">module.exports = require('./cjs/index.cjs.js');</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span></code></pre><p>As distributing two distinct folders leads to having no more entry files in the <code>dist</code> path of the library, I also like to add two files that re-export the code. It can be useful when importing the library in a project.</p>
<p>In addition, the <code>package.json</code> entries should be updated accordingly as well.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">name</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">mylibary</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">version</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">0.0.1</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">main</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">dist/cjs/index.cjs.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">module</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">dist/esm/index.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">types</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">dist/types/index.d.ts</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h2>CSS and SASS</h2>
<p>Did you know that esbuild can bundle <a href="https://esbuild.github.io/content-types/#css" rel="noopener noreferrer external">CSS</a> files too? There is even a <a href="https://github.com/glromeo/esbuild-sass-plugin" rel="noopener noreferrer external">SASS plugin</a> that makes it easy to build <code>.scss</code> files 😃.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> i</span><span style="color:#BD93F9"> -D</span><span style="color:#F1FA8C"> esbuild-sass-plugin</span><span style="color:#F1FA8C"> postcss</span><span style="color:#F1FA8C"> autoprefixer</span><span style="color:#F1FA8C"> postcss-preset-env</span></span></code></pre><p>In following example, I bundle two different SASS files - <code>src/index.scss</code> and <code>src/doc/index.scss</code>. I use the plugin to transform the code - i.e. to prefix the CSS - and I also use the option <a href="https://esbuild.github.io/api/#metafile" rel="noopener noreferrer external">metafile</a> which tells esbuild to produce some metadata about the build in JSON format.</p>
<p>Using it, I can retrieve the paths and names of the generated CSS files to e.g. include these in my HTML files later on.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> esbuild </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">esbuild</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { sassPlugin } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">esbuild-sass-plugin</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> postcss </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">postcss</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> autoprefixer </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">autoprefixer</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> postcssPresetEnv </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">postcss-preset-env</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> buildCSS</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { metafile } </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> esbuild.</span><span style="color:#50FA7B">build</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        entryPoints</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">src/index.scss</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">src/doc/index.scss</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">        bundle</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        minify</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        format</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">esm</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        target</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">esnext</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">        outdir</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">dist/build</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        metafile</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        plugins</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#50FA7B">            sassPlugin</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#FF79C6">                async</span><span style="color:#50FA7B"> transform</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">source</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">resolveDir</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">                    const</span><span style="color:#F8F8F2"> { css } </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> postcss</span><span style="color:#F8F8F2">([autoprefixer, </span><span style="color:#50FA7B">postcssPresetEnv</span><span style="color:#F8F8F2">()]).</span><span style="color:#50FA7B">process</span><span style="color:#F8F8F2">(source, {</span></span>
<span class="line"><span style="color:#F8F8F2">                        from</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> undefined</span></span>
<span class="line"><span style="color:#F8F8F2">                    });</span></span>
<span class="line"><span style="color:#FF79C6">                    return</span><span style="color:#F8F8F2"> css;</span></span>
<span class="line"><span style="color:#F8F8F2">                }</span></span>
<span class="line"><span style="color:#F8F8F2">            })</span></span>
<span class="line"><span style="color:#F8F8F2">        ]</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { outputs } </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> metafile;</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> Object.</span><span style="color:#50FA7B">keys</span><span style="color:#F8F8F2">(outputs);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><hr />
<h2>Conclusion</h2>
<p><a href="https://esbuild.github.io/" rel="noopener noreferrer external">esbuild</a> is still slick!</p>
<p>To infinity and beyond<br />
David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Create A Popover In Svelte]]></title>
          <description><![CDATA[How to create a popover in Svelte without any other libraries or dependencies.]]></description>
          <link>https://daviddalbusco.com/blog/create-a-popover-in-svelte/</link>
          <pubDate>Thu, 23 Jun 2022 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://images.unsplash.com/photo-1620545628446-6319bce6b95c?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwzMDkyMzV8MHwxfHNlYXJjaHwzMHx8Y3JlYXRlfGVufDB8fHx8MTY1NTk2MjEzMw&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=1080" alt="Street art, using the word create." / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@jannerboy62?utm_source=Papyrs&amp;utm_medium=referral" rel="noopener noreferrer external">Nick Fewings</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<hr />
<p>I have implemented the UI components of my last project <a href="https://papy.rs/" rel="noopener noreferrer external">Papyrs</a> without any third party design system libraries - i.e. I created all the components from scratch. I did so to get full control and flexibility over the miscellaneous bricks of my opinionated layout 😁.</p>
<p>In this blog post, I share how you can develop a popover component in <a href="https://svelte.dev/" rel="noopener noreferrer external">Svelte</a>.</p>
<p><img src="https://6zvwc-sqaaa-aaaal-aalma-cai.raw.ic0.app/images/popover.gif?token=zSiVJ0hkcizwlQaBH2mc3" alt="popover.gif" / loading="lazy"></p>
<hr />
<h2>Skeleton</h2>
<p>A popover is a floating container that is rendered over the content next to an anchor - commonly a button - which initiates its display. To improve the visual focus of the overlay, a backdrop is generally used to partially obfuscate the view behind it.</p>
<p><img src="https://6zvwc-sqaaa-aaaal-aalma-cai.raw.ic0.app/images/excalidraw-1655964556754.webp?token=2HjwiL6BcBFxbz4T4OamD" alt="excalidraw-1655964556754.webp" / loading="lazy"></p>
<p>We can start the implementation by replicating above skeleton in a component named <code>Popover.svelte</code> which contains a <code>button</code> and <code>div</code>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">button</span><span style="color:#F8F8F2">>Open&#x3C;/</span><span style="color:#FF79C6">button</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">div</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    role</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">dialog</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    aria-labelledby</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Title</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    aria-describedby</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Description</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    aria-orientation</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">vertical</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">>Backdrop&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">>Content&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span></code></pre><p>To improve the accessibility we can set the <code>dialog</code> role and provide some <code>aria</code> information (see <a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/dialog_role" rel="noopener noreferrer external">MDN docs</a> for more details).</p>
<hr />
<h2>Animation</h2>
<p>We create a <code>boolean</code> state - <code>visible</code> - to display or close the popover. When the <code>button</code> is clicked, the state is set to <code>true</code> and the overlay gets rendered. On the contrary, when the backdrop is clicked, it turns to <code>false</code> and it closes.</p>
<p>In addition, we add a click listener on the popover that does nothing else than stopping the event propagation. This is useful to avoid closing the overlay when user is interacting with its content.</p>
<p>We can also make the overlay appearing and disappearing gracefully thanks to the <a href="https://svelte.dev/tutorial/transition" rel="noopener noreferrer external">transition directive</a> - also known as &quot;Svelte's black magic&quot; 😁.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#50FA7B;font-style:italic"> lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  import </span><span style="color:#FF79C6">{</span><span style="color:#F8F8F2"> fade, scale </span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2"> from 'svelte/transition';</span></span>
<span class="line"><span style="color:#F8F8F2">  import </span><span style="color:#FF79C6">{</span><span style="color:#F8F8F2"> quintOut </span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2"> from 'svelte/easing';</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  let visible = false;</span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">button</span><span style="color:#50FA7B;font-style:italic"> on</span><span style="color:#FF79C6">:</span><span style="color:#50FA7B;font-style:italic">click</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> (visible </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">>Open&#x3C;/</span><span style="color:#FF79C6">button</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">{#</span><span style="color:#FF79C6">if</span><span style="color:#F8F8F2"> visible}</span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;</span><span style="color:#FF79C6">div</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    role</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">dialog</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    aria-labelledby</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Title</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    aria-describedby</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Description</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    aria-orientation</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">vertical</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    transition</span><span style="color:#FF79C6">:</span><span style="color:#50FA7B;font-style:italic">fade</span></span>
<span class="line"><span>    </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">on:click|stopPropagation</span></span>
<span class="line"><span style="color:#F8F8F2">  ></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">div</span></span>
<span class="line"><span>      </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">on:click|stopPropagation={()</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">=></span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">(visible</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">=</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">false)}</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">      transition</span><span style="color:#FF79C6">:</span><span style="color:#50FA7B;font-style:italic">scale</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">{ delay</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 25</span><span style="color:#F8F8F2">, duration</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 150</span><span style="color:#F8F8F2">, easing</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> quintOut }</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#F8F8F2">    ></span></span>
<span class="line"><span style="color:#F8F8F2">      Backdrop</span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">>Content&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">{</span><span style="color:#FF79C6">/if</span><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h2>Position over the content</h2>
<p>The popover should be rendered over the all content regardless if the page has been scrolled or not. Therefore we can use a <code>fixed</code> position as starting point. Its content and backdrop are both set to an <code>absolute</code> positioning. The backdrop should cover the screen as well but is a child of the overlay - therefore the &quot;absolute&quot; - and the content should be positioned next to the anchor.</p>
<p>The rest of the CSS code we add to our solution are minimal styling settings for width, height or colors.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#50FA7B;font-style:italic"> lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  import </span><span style="color:#FF79C6">{</span><span style="color:#F8F8F2"> fade, scale </span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2"> from 'svelte/transition';</span></span>
<span class="line"><span style="color:#F8F8F2">  import </span><span style="color:#FF79C6">{</span><span style="color:#F8F8F2"> quintOut </span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2"> from 'svelte/easing';</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  let visible = false;</span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">button</span><span style="color:#50FA7B;font-style:italic"> on</span><span style="color:#FF79C6">:</span><span style="color:#50FA7B;font-style:italic">click</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> (visible </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">>Open&#x3C;/</span><span style="color:#FF79C6">button</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">{#</span><span style="color:#FF79C6">if</span><span style="color:#F8F8F2"> visible}</span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;</span><span style="color:#FF79C6">div</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    role</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">dialog</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    aria-labelledby</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Title</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    aria-describedby</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Description</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    aria-orientation</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">vertical</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    transition</span><span style="color:#FF79C6">:</span><span style="color:#50FA7B;font-style:italic">fade</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    class</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">popover</span><span style="color:#E9F284">"</span></span>
<span class="line"><span>    </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">on:click|stopPropagation</span></span>
<span class="line"><span style="color:#F8F8F2">  ></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">div</span></span>
<span class="line"><span>      </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">on:click|stopPropagation={()</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">=></span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">(visible</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">=</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">false)}</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">      transition</span><span style="color:#FF79C6">:</span><span style="color:#50FA7B;font-style:italic">scale</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">{ delay</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 25</span><span style="color:#F8F8F2">, duration</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 150</span><span style="color:#F8F8F2">, easing</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> quintOut }</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">      class</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">backdrop</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    /></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> class</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">wrapper</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Content&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">{</span><span style="color:#FF79C6">/if</span><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">style</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  .popover </span><span style="color:#FF79C6">{</span></span>
<span class="line"><span style="color:#F8F8F2">    position: fixed;</span></span>
<span class="line"><span style="color:#F8F8F2">    inset: </span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    z</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">index: </span><span style="color:#BD93F9">997</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  .backdrop </span><span style="color:#FF79C6">{</span></span>
<span class="line"><span style="color:#F8F8F2">    position: absolute;</span></span>
<span class="line"><span style="color:#F8F8F2">    inset: </span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    background: </span><span style="color:#50FA7B">rgba</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">0.3</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  .wrapper </span><span style="color:#FF79C6">{</span></span>
<span class="line"><span style="color:#F8F8F2">    position: absolute;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    min</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">width: 200px;</span></span>
<span class="line"><span style="color:#F8F8F2">    max</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">width: 200px;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    min</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">height: 100px;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    width: fit</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">content;</span></span>
<span class="line"><span style="color:#F8F8F2">    height: auto;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    overflow: hidden;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    display: flex;</span></span>
<span class="line"><span style="color:#F8F8F2">    flex</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">direction: column;</span></span>
<span class="line"><span style="color:#F8F8F2">    align</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">items: flex</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">start;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    background: white;</span></span>
<span class="line"><span style="color:#F8F8F2">    color: black;</span></span>
<span class="line"><span style="color:#FF79C6">  }</span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">style</span><span style="color:#F8F8F2">></span></span></code></pre><hr />
<h2>Position next to the anchor</h2>
<p>To set the overlay next to the button we have to get a reference on that element to find its position within the viewport. For such purpose we can <code>bind</code> the anchor.</p>
<p>When the reference is ready or when the window is resized (the position might change if user resize the browser), we use the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect" rel="noopener noreferrer external">getBoundingClientRect()</a> method to query the information about the position.</p>
<p>We ultimately translate these JavaScript information to CSS variables to render the content of the popover at the exact position we would like to set.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#50FA7B;font-style:italic"> lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  // ...</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  let anchor: HTMLButtonElement | undefined = undefined;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  let bottom: number;</span></span>
<span class="line"><span style="color:#F8F8F2">  let left: number;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  const initPosition = () =></span></span>
<span class="line"><span style="color:#F8F8F2">    (</span><span style="color:#FF79C6">{</span><span style="color:#F8F8F2"> bottom, left </span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2"> = anchor?.getBoundingClientRect() ?? </span><span style="color:#FF79C6">{</span><span style="color:#F8F8F2"> bottom: </span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">, left: </span><span style="color:#BD93F9">0</span><span style="color:#FF79C6"> }</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  $: anchor, initPosition();</span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">svelte:window</span><span style="color:#50FA7B;font-style:italic"> on</span><span style="color:#FF79C6">:</span><span style="color:#50FA7B;font-style:italic">resize</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">initPosition</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">button</span><span style="color:#50FA7B;font-style:italic"> on</span><span style="color:#FF79C6">:</span><span style="color:#50FA7B;font-style:italic">click</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> (visible </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">}</span><span style="color:#50FA7B;font-style:italic"> bind</span><span style="color:#FF79C6">:</span><span style="color:#50FA7B;font-style:italic">this</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">anchor</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">>Open&#x3C;/</span><span style="color:#FF79C6">button</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">{#</span><span style="color:#FF79C6">if</span><span style="color:#F8F8F2"> visible}</span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;</span><span style="color:#FF79C6">div</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    role</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">dialog</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    aria-labelledby</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Title</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    aria-describedby</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Description</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    aria-orientation</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">vertical</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    transition</span><span style="color:#FF79C6">:</span><span style="color:#50FA7B;font-style:italic">fade</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    class</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">popover</span><span style="color:#E9F284">"</span></span>
<span class="line"><span>    </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">on:click|stopPropagation</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    style</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">--popover-top: {`${bottom}px`}; --popover-left: {`${left}px`}</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">  ></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;!-- ... --></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">{</span><span style="color:#FF79C6">/if</span><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">style</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  /** ... */</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  .wrapper </span><span style="color:#FF79C6">{</span></span>
<span class="line"><span style="color:#F8F8F2">    position: absolute;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    top: </span><span style="color:#50FA7B">calc</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">var</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">--</span><span style="color:#F8F8F2">popover</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">top) </span><span style="color:#FF79C6">+</span><span style="color:#F8F8F2"> 10px);</span></span>
<span class="line"><span style="color:#F8F8F2">    left: </span><span style="color:#50FA7B">var</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">--</span><span style="color:#F8F8F2">popover</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">left);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    /** ... */</span></span>
<span class="line"><span style="color:#FF79C6">  }</span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">style</span><span style="color:#F8F8F2">></span></span></code></pre><p>Above code snippet was trimmed to showcase only what is related to this chapter. Altogether, the component's code is the following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#50FA7B;font-style:italic"> lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  import </span><span style="color:#FF79C6">{</span><span style="color:#F8F8F2"> fade, scale </span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2"> from 'svelte/transition';</span></span>
<span class="line"><span style="color:#F8F8F2">  import </span><span style="color:#FF79C6">{</span><span style="color:#F8F8F2"> quintOut </span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2"> from 'svelte/easing';</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  let visible = false;</span></span>
<span class="line"><span style="color:#F8F8F2">  let anchor: HTMLButtonElement | undefined = undefined;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  let bottom: number;</span></span>
<span class="line"><span style="color:#F8F8F2">  let left: number;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  const initPosition = () =></span></span>
<span class="line"><span style="color:#F8F8F2">    (</span><span style="color:#FF79C6">{</span><span style="color:#F8F8F2"> bottom, left </span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2"> = anchor?.getBoundingClientRect() ?? </span><span style="color:#FF79C6">{</span><span style="color:#F8F8F2"> bottom: </span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">, left: </span><span style="color:#BD93F9">0</span><span style="color:#FF79C6"> }</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  $: anchor, initPosition();</span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">svelte:window</span><span style="color:#50FA7B;font-style:italic"> on</span><span style="color:#FF79C6">:</span><span style="color:#50FA7B;font-style:italic">resize</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">initPosition</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">button</span><span style="color:#50FA7B;font-style:italic"> on</span><span style="color:#FF79C6">:</span><span style="color:#50FA7B;font-style:italic">click</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> (visible </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">}</span><span style="color:#50FA7B;font-style:italic"> bind</span><span style="color:#FF79C6">:</span><span style="color:#50FA7B;font-style:italic">this</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">anchor</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">>Open&#x3C;/</span><span style="color:#FF79C6">button</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">{#</span><span style="color:#FF79C6">if</span><span style="color:#F8F8F2"> visible}</span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;</span><span style="color:#FF79C6">div</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    role</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">dialog</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    aria-labelledby</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Title</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    aria-describedby</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Description</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    aria-orientation</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">vertical</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    transition</span><span style="color:#FF79C6">:</span><span style="color:#50FA7B;font-style:italic">fade</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    class</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">popover</span><span style="color:#E9F284">"</span></span>
<span class="line"><span>    </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">on:click|stopPropagation</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    style</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">--popover-top: {`${bottom}px`}; --popover-left: {`${left}px`}</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">  ></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">div</span></span>
<span class="line"><span>      </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">on:click|stopPropagation={()</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">=></span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">(visible</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">=</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">false)}</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">      transition</span><span style="color:#FF79C6">:</span><span style="color:#50FA7B;font-style:italic">scale</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">{ delay</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 25</span><span style="color:#F8F8F2">, duration</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 150</span><span style="color:#F8F8F2">, easing</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> quintOut }</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">      class</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">backdrop</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    /></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> class</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">wrapper</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Content&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">{</span><span style="color:#FF79C6">/if</span><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">style</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  .popover </span><span style="color:#FF79C6">{</span></span>
<span class="line"><span style="color:#F8F8F2">    position: fixed;</span></span>
<span class="line"><span style="color:#F8F8F2">    inset: </span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    z</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">index: </span><span style="color:#BD93F9">997</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  .backdrop </span><span style="color:#FF79C6">{</span></span>
<span class="line"><span style="color:#F8F8F2">    position: absolute;</span></span>
<span class="line"><span style="color:#F8F8F2">    inset: </span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    background: </span><span style="color:#50FA7B">rgba</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">0.3</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  .wrapper </span><span style="color:#FF79C6">{</span></span>
<span class="line"><span style="color:#F8F8F2">    position: absolute;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    top: </span><span style="color:#50FA7B">calc</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">var</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">--</span><span style="color:#F8F8F2">popover</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">top) </span><span style="color:#FF79C6">+</span><span style="color:#F8F8F2"> 10px);</span></span>
<span class="line"><span style="color:#F8F8F2">    left: </span><span style="color:#50FA7B">var</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">--</span><span style="color:#F8F8F2">popover</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">left);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    min</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">width: 200px;</span></span>
<span class="line"><span style="color:#F8F8F2">    max</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">width: 200px;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    min</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">height: 100px;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    width: fit</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">content;</span></span>
<span class="line"><span style="color:#F8F8F2">    height: auto;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    overflow: hidden;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    display: flex;</span></span>
<span class="line"><span style="color:#F8F8F2">    flex</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">direction: column;</span></span>
<span class="line"><span style="color:#F8F8F2">    align</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">items: flex</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">start;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    background: white;</span></span>
<span class="line"><span style="color:#F8F8F2">    color: black;</span></span>
<span class="line"><span style="color:#FF79C6">  }</span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">style</span><span style="color:#F8F8F2">></span></span></code></pre><p>And...that's it! We have implemented a bare minimum custom popover that can be used in any Svelte applications without any dependency.</p>
<hr />
<h2>Conclusion</h2>
<p>We did not beautify the solution, therefore the outcome remains visually rough on the edges but, the popover works out as expected.</p>
<p>To iterate from there, implement more options or make it shiny, you could for example have a look to the open source code of <a href="https://papy.rs" rel="noopener noreferrer external">Papyrs</a> on <a href="https://github.com/papyrs/papyrs" rel="noopener noreferrer external">GitHub</a> 🤗.</p>
<p>To infinity and beyond<br />
David</p>
<p><img src="https://6zvwc-sqaaa-aaaal-aalma-cai.raw.ic0.app/images/popover-tutorial.gif?token=2-v9JeqR2Kp0TlXsAEQIS" alt="popover-tutorial.gif" / loading="lazy"></p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Announcing Papyrs: Blog on web3]]></title>
          <description><![CDATA[I have developed a new open-source, privacy-first, decentralized blogging platform and today I am making it available to anyone.]]></description>
          <link>https://daviddalbusco.com/blog/announcing-papyrs-blog-on-web3/</link>
          <pubDate>Thu, 16 Jun 2022 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>So, I have developed a new open-source, privacy-first, decentralized blogging platform that lives 100% on chain and today I am making it available to anyone. I called it: <a href="https://app.papy.rs" rel="noopener noreferrer external">Papyrs</a>.</p>
<hr />
<h2>What a ride!</h2>
<p>Last year, I was lucky enough to receive grants from the DFINITY foundation to port our web editor for slides, <a href="https://deckdeckgo.com/" rel="noopener noreferrer external">DeckDeckGo</a>, to the <a href="https://smartcontracts.org/" rel="noopener noreferrer external">Internet Computer</a>.</p>
<p>While I completed all milestones, I did not migrate it yet.</p>
<p>Along the way, I had the idea of extending the platform to support documents. One thing leading to another, I ended developing my own rich text editor library, <a href="https://stylojs.com/" rel="noopener noreferrer external">Stylo</a>, and decided to focus first on this new format; which is why today I'm launching a new blogging platform.</p>
<p>On the one hand, thanks to this approach, I limit the number of features I launch on the IC, which allows me to gain experience to later deploy our presentation editor more serenely.</p>
<p>On the other hand, I like to develop new ideas and thus double the fun 😜.</p>
<hr />
<h2>Manifesto</h2>
<p>So, why launch another blogging application? After-all there are already many platforms that allow you to write articles, right 🤷‍♂️?</p>
<p>Sure there is but none are following the guidelines that drive me.</p>
<hr />
<h3>Decentralization &amp; blockchain</h3>
<p>The concept of decentralization - or web3 - is linked to a certain form of romanticism for developers that <a href="https://twitter.com/cdixon" rel="noopener noreferrer external">Chris Dixon</a> summarizes in following terms:</p>
<p><em>The lesson is that when you compare centralized and decentralized systems you need to consider them dynamically, as processes, instead of statically, as rigid products. Centralized systems often start out fully baked, but only get better at the rate at which employees at the sponsoring company improve them. Decentralized systems start out half-baked but, under the right conditions, grow exponentially as they attract new contributors (<a href="https://twg.io/whiteboard/why-decentralization-matters/" rel="noopener noreferrer external">source</a>).</em></p>
<p>As a programmer myself, this idea is appealing. I also think that blockchain technology might lead to interesting new perspectives for the future.</p>
<p>I can for instance imagine that consensus and other certification mechanisms could potentially be useful to prove the validity of the articles shared with Papyrs - i.e. could be used to prevent fake news and misinformation.</p>
<p>But that is for the future. Right now, my main commitment is empowering users ownership of their data by taking advantages of smart contracts.</p>
<p>Between ads networks - that I cannot stand - and Big Tech companies that exploit our private data, I developed a bit a pessimist view on the what the Internet has become. Furthermore, as my parents say when they are pessimistic, it also feels that &quot;the best is not to come&quot;.</p>
<p>That's why I developed Papyrs with a true privacy first approach.</p>
<hr />
<h3>Privacy-first</h3>
<p>The editor works primarily offline and requires no sign-in. The blog posts can be loaded and exported to the file system. There is a Chrome <a href="https://chrome.google.com/webstore/detail/papyrs-to-markdown/caacmbgdcjpdpmccocmbiljodkbkjglh" rel="noopener noreferrer external">plugin</a> to convert these to markdown.</p>
<p>If you are interested in storing and sharing your blog posts online - on chain - then an authentication with <a href="https://identity.ic0.app/" rel="noopener noreferrer external">Internet Identity</a> is required.</p>
<p>However, unlike any other auth providers, using this powerful password-less method preserves anonymity!</p>
<p>Finally, as currently implemented, I - as the admin of the platform - have absolutely no privileges to read your data. As long as you do not share any content publicly, it remains fully private.</p>
<p>Your data are your own, period.</p>
<hr />
<h3>Open-source</h3>
<p>In my opinion, there can be no privacy without open source code. It is a matter of trust.</p>
<p>Therefore, here are all the links to each and every single lines of code I wrote for Papyrs:</p>
<ul>
<li><p><a href="https://github.com/papyrs/papyrs" rel="noopener noreferrer external">Papyrs</a>: the main repo - the web application</p></li>
<li><p><a href="https://github.com/papyrs/ic" rel="noopener noreferrer external">IC</a>: canisters and sign-in providers</p></li>
<li><p><a href="https://github.com/papyrs/cdn" rel="noopener noreferrer external">CDN</a>: libraries and assets used across the platform</p></li>
<li><p><a href="https://github.com/papyrs/unsplash-proxy" rel="noopener noreferrer external">Unsplash-proxy</a>: a proxy used to query Unsplash</p></li>
<li><p><a href="https://github.com/papyrs/stylo" rel="noopener noreferrer external">Stylo</a>: another kind of rich text editor</p></li>
<li><p><a href="https://github.com/papyrs/kit" rel="noopener noreferrer external">Kit</a>: the templates for the posts that are published on chain</p></li>
<li><p><a href="https://github.com/papyrs/markdown-plugin" rel="noopener noreferrer external">Papyrs to markdown</a>: a Chrome plugin to convert to markdown</p></li>
<li><p><a href="https://github.com/deckgo/deckdeckgo/" rel="noopener noreferrer external">DeckDeckGo</a>: various web components, utilities and the providers for the offline persistence and synchronization of the data</p></li>
</ul>
<p>As the saying goes, &quot;sharing is caring&quot; 🤗. I hope what I implemented can help any developer starts building on the Internet Computer. Furthermore, I am also looking forward to feedbacks and contributions (🤞) in order to improve the project.</p>
<hr />
<h3>Indie hacking</h3>
<p>Beside my opinionated point of view and the fact that I am the first user of my own project, I would lie if I would not admit that I have also some small hopes for Papyrs to generate revenue in the future.</p>
<p>All this cannot happen without users but if some would be interested, I am convinced there will be a way to find a model that can be both profitable for Papyrs and the content creators while absolutely making no concession on my values.</p>
<hr />
<h2>Features</h2>
<p>As mentioned in the previous chapter, I use the platform for blogging purpose myself. That is why I designed it the way I want with the features I often need.</p>
<p>e.g. browsing <a href="https://unsplash.com/" rel="noopener noreferrer external">Unsplash</a> to select images for presentation purpose.</p>
<p><img src="https://images.unsplash.com/photo-1514984879728-be0aff75a6e8?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwzMDkyMzV8MHwxfHNlYXJjaHw0OXx8ZG9nJTIwaGVsbG98ZW58MHx8fHwxNjU0NDMzNDEy&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=1080" alt="https://unsplash.com/photos/uNNCs5kL70Q" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@jamie452?utm_source=Papyrs&amp;utm_medium=referral" rel="noopener noreferrer external">Jamie Street</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>Adding GIFs from <a href="https://tenor.com/" rel="noopener noreferrer external">Tenor</a> for comic relief.</p>
<p><img src="https://media.tenor.com/images/e82c2cd09db0bf410917cda2ef22ffd4/tenor.gif" alt="a good laugh" / loading="lazy"></p>
<p>Showcasing code snippets and editing these with the <a href="https://microsoft.github.io/monaco-editor/" rel="noopener noreferrer external">Monaco editor</a>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> addParagraph</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ({</span></span>
<span class="line"><span style="color:#F8F8F2">    paragraph,</span></span>
<span class="line"><span style="color:#F8F8F2">    container,</span></span>
<span class="line"><span style="color:#F8F8F2">    fragment</span></span>
<span class="line"><span style="color:#F8F8F2">}</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    container</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLElement</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    paragraph</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLElement</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    fragment</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> DocumentFragment</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">})</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Node</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Node</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">>((</span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> addObserver </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> MutationObserver</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">mutations</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> MutationRecord</span><span style="color:#F8F8F2">[]) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">            addObserver.</span><span style="color:#50FA7B">disconnect</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">            resolve</span><span style="color:#F8F8F2">(mutations[</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">]?.addedNodes?.[</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">]);</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        addObserver.</span><span style="color:#50FA7B">observe</span><span style="color:#F8F8F2">(container, { childList</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">, subtree</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2"> });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        paragraph.</span><span style="color:#50FA7B">after</span><span style="color:#F8F8F2">(fragment);</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Sketching hand-draw like diagrams with <a href="https://excalidraw.com/" rel="noopener noreferrer external">Excalidraw</a>.</p>
<p><img src="https://6zvwc-sqaaa-aaaal-aalma-cai.raw.ic0.app/images/excalidraw-1655146692059.webp?token=Pc79eylT4f4H9_sg_mIlt" alt="https://6zvwc-sqaaa-aaaal-aalma-cai.raw.ic0.app/images/excalidraw-1655146692059.webp?token=Pc79eylT4f4H9_sg_mIlt" / loading="lazy"></p>
<p>And more to come...</p>
<hr />
<h2>Call for writers</h2>
<p>Today, my blogging platform is becoming accessible to everyone, but that doesn't mean that it really lives. It needs a community, it needs content, it needs writers, it needs YOU!</p>
<p>Get started now to write your next blog posts: <a href="https://app.papy.rs" rel="noopener noreferrer external">https://app.papy.rs</a></p>
<p>To infinity and beyond</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Engelberger Rostock]]></title>
          <link>https://daviddalbusco.com/trails/2022/engelberger-rostock/</link>
          <pubDate>Thu, 16 Jun 2022 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>Wonderful trail in the region of Engelberg around the mountains Ruchstock, Spitz Mann and Walenstöck.</p>
<p>The path follows mountain (white - red - white) and alpin (white - blue - white) signs. Few yellow signs.</p>
<p>There were still lots of névé (snow) going up in June, therefore the path is quite risky!!!</p>
<hr />
<p>The journey starts and ends in Oberrickenbach. Accessible with Post bus or car (there is a parking). From there, take the cable car to Kreuzhütte. If you wish to go up by foot too, it's feasible.</p>
<p>At Kreuhütte, the trail effectively starts. Follows the direction of Rugghubelhütte. There won't be any more yellow signs for a while, follow mountain sign.</p>
<p>Path to Engelberger Rostock is not indicated neither, at least did not find it. When arriving on a ridge after a quite long climb, take left. Note that part can be skipped but the 2.5km and 340m +/- it adds are challenging and worth the effort.</p>
<p>From Engelberger Rostock, go back the same path you used to get there till the previous ridge.</p>
<p>Rest of the trail becomes finally easier to follow. Go to Rugghubelhütte, Brunnihütte, Walenalp and back to Oberrickenbach.</p>
<hr />
<p>The way down was particularly long for my old knees (-2200m). Because of the snow and signs I had to look for the route and go back and forth few times (gpx track is probably a bit funky). Therefore I was quite slow but what a lovely day ☀️🏔.</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Dynamically Create Canister Smart Contracts In Motoko]]></title>
          <description><![CDATA[How to generate on-the-fly decentralized smart contracts on the Internet Computer.]]></description>
          <link>https://daviddalbusco.com/blog/dynamically-create-canister-smart-contracts-in-motoko/</link>
          <pubDate>Tue, 10 May 2022 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*0aVC6yVZLehL990fUJDazw.png" alt="" / loading="lazy"></p>
<hr />
<p>In one of my last <a href="https://6zvwc-sqaaa-aaaal-aalma-cai.raw.ic0.app/d/call-internet-computer-canister-smart-contracts-in-nodejs" rel="noopener noreferrer external">blog posts</a>, I shared a solution to query smart contracts on <a href="https://smartcontracts.org/" rel="noopener noreferrer external">Internet Computer</a> in a NodeJS context.</p>
<p>That article was the first of a series that will display the various scripts I have developed for new project called <a href="https://papy.rs" rel="noopener noreferrer external">Papyrs</a> - an open-source, privacy-first, decentralized blogging platform that lives 100% on chain.</p>
<p>I plan to share how I query remaining cycles and update the code of my users’ canisters. But first I’m going to share the basis of the architecture — i.e., to demonstrate the code that allows a program to dynamically create canisters.</p>
<hr />
<h2>Architecture</h2>
<p>Unlike web2 projects that centralize user data in a single — or distributed — database, I adopted a more futuristic approach for the data persistence of Papyrs.</p>
<p>A main actor acts as a a manager that — on the fly, upon object creation — generates a decentralized, secure simple key-value database-like for each single data persistence of each user.</p>
<p><img src="https://6zvwc-sqaaa-aaaal-aalma-cai.raw.ic0.app/images/manager-buckets.png?token=CS-cusHrUKnmwvP6TDNpy" alt="" / loading="lazy"></p>
<p>In following chapters I introduce such a solution with a sample project developed in <a href="https://github.com/dfinity/motoko" rel="noopener noreferrer external">Motoko</a>.</p>
<p><em>For more details about this architecture, you can read an article I wrote about it: <a href="https://daviddalbusco.com/blog/internet-computer-web-app-decentralized-database-architecture" rel="noopener noreferrer external">Internet Computer: Web App Decentralized Database Architecture</a>.</em></p>
<hr />
<h2>Bucket</h2>
<p>Each user gets a dedicated canister smart contract which I name a <code>bucket</code> to follow the convention of the actor classes example provided by DFINITY on <a href="https://github.com/dfinity/examples/tree/master/motoko/classes" rel="noopener noreferrer external">GitHub</a>. On the <a href="https://forum.dfinity.org/" rel="noopener noreferrer external">forum</a> it might sometimes also be referenced as &quot;child&quot; canister.</p>
<p>For demo purposes, I thought that such a bucket - an <code>actor</code> - could expose a function that <code>say</code> hello. Furthermore, to anticipate my future writings as well, it also receives a <code>user</code> as parameter and contains a non stable <code>version</code> number that could be incremented each time a new canister's code is installed.</p>
<p>Note that I use a <code>Text</code> type for the user only to simplify the sample. In a real use case I would use a <code>Principal</code>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> Nat </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">mo:base/Nat</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">actor class Bucket(user: Text) = this {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    var version: Nat = 1;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    public query func say() : async Text {</span></span>
<span class="line"><span style="color:#F8F8F2">      return </span><span style="color:#F1FA8C">"Hello World - "</span><span style="color:#F8F8F2"> # user # </span><span style="color:#F1FA8C">" - v"</span><span style="color:#F8F8F2"> # Nat.toText(version);</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h2>Manager</h2>
<p>The manager - or <code>Main</code> actor - contains more logic, so I will break it down into steps before presenting it in its entirety.</p>
<p>As mentioned before, it creates dynamic canisters. Therefore it can or should, depending on the use case, keep track of those that have been created — e.g., by stacking the canister ID that have been created within an <code>Hashmap</code>.</p>
<p>However, for the sake of simplicity, in this article I only keep track of the last smart contract that has been initialized with the help of a stable variable <code>canisterId</code>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">actor Main {</span></span>
<span class="line"><span style="color:#F8F8F2">  private stable </span><span style="color:#FF79C6">var</span><span style="color:#F8F8F2"> canisterId</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> ?</span><span style="color:#8BE9FD;font-style:italic">Principal</span><span style="color:#FF79C6"> =</span><span style="color:#BD93F9"> null</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  public query func </span><span style="color:#50FA7B">getCanisterId</span><span style="color:#F8F8F2">() : async </span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2">Principal {</span></span>
<span class="line"><span style="color:#F8F8F2">    canisterId</span></span>
<span class="line"><span style="color:#F8F8F2">  };</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><hr />
<p>To create a new canister we mainly need two things:</p>
<ul>
<li>the bucket - i.e., the actor of previous chapter</li>
<li>the cycles <a href="https://smartcontracts.org/docs/current/references/motoko-ref/experimentalcycles" rel="noopener noreferrer external">library</a></li>
</ul>
<p>Because we implement the code of the bucket in the same project, we can include it with a relative import path. Each call to <code>Bucket.Bucket(param)</code> instantiates a new bucket — i.e., dynamically creates a new canister smart contract.</p>
<p>The library is used to share the manager’s cycles with the bucket it creates. The related computational cost is 100,000,000,000 cycles — i.e., around $0.142, according to <a href="https://smartcontracts.org/docs/current/developer-docs/updates/computation-and-storage-costs" rel="noopener noreferrer external">documentation</a>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> Cycles </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">mo:base/ExperimentalCycles</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">import Principal </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">mo:base/Principal</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">import Error </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">mo:base/Error</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">import Bucket </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">./bucket</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">actor Main {</span></span>
<span class="line"><span style="color:#F8F8F2">  private stable var canisterId: ?Principal = null;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  public shared({ caller }) func init(): async (Principal) {</span></span>
<span class="line"><span style="color:#F8F8F2">    Cycles.add(1_000_000_000_000);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    let b = await Bucket.Bucket(</span><span style="color:#F1FA8C">"User1"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    canisterId := ?(Principal.fromActor(b));</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    switch (canisterId) {</span></span>
<span class="line"><span style="color:#F8F8F2">      case null {</span></span>
<span class="line"><span style="color:#F8F8F2">        throw Error.reject(</span><span style="color:#F1FA8C">"Bucket init error"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">      };</span></span>
<span class="line"><span style="color:#F8F8F2">      case (?canisterId) {</span></span>
<span class="line"><span style="color:#F8F8F2">        return canisterId;</span></span>
<span class="line"><span style="color:#F8F8F2">      };</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"><span style="color:#F8F8F2">  };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  public query func getCanisterId() : async ?Principal {</span></span>
<span class="line"><span style="color:#F8F8F2">    canisterId</span></span>
<span class="line"><span style="color:#F8F8F2">  };</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><hr />
<p>While this might be the end of the story, I would like to add another piece to the puzzle.</p>
<p>Indeed, it might be interesting to set the controllers that can modify the bucket — e.g., it might be interesting to allow your principal and/or the one of the manager to update the code of the canisters.</p>
<p>For such purpose, we first need to add the specification of the Internet Computer to the project in form of a new Motoko <code>module</code>. You can either convert the <a href="https://github.com/dfinity/interface-spec/blob/master/spec/ic.did" rel="noopener noreferrer external">candid</a> file or grab the one I used in Papyrs (<a href="https://github.com/papyrs/ic/blob/main/canisters/src/types/ic.types.mo" rel="noopener noreferrer external">source</a>).</p>
<p>Finally, we can declare a variable that will be used to call the IC management canister address (<code>aaaaa-aa</code>) and use it to effectively update the settings of the newly created canister.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> Cycles </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">mo:base/ExperimentalCycles</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">import Principal </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">mo:base/Principal</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">import Error </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">mo:base/Error</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">import IC </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">./ic.types</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">import Bucket </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">./bucket</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">actor Main {</span></span>
<span class="line"><span style="color:#F8F8F2">  private stable var canisterId: ?Principal = null;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  private let ic : IC.Self = actor </span><span style="color:#F1FA8C">"aaaaa-aa"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  public shared({ caller }) func init(): async (Principal) {</span></span>
<span class="line"><span style="color:#F8F8F2">    Cycles.add(1_000_000_000_000);</span></span>
<span class="line"><span style="color:#F8F8F2">    let b = await Bucket.Bucket(</span><span style="color:#F1FA8C">"User1"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    canisterId := ?(Principal.fromActor(b));</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    switch (canisterId) {</span></span>
<span class="line"><span style="color:#F8F8F2">      case null {</span></span>
<span class="line"><span style="color:#F8F8F2">        throw Error.reject(</span><span style="color:#F1FA8C">"Bucket init error"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">      };</span></span>
<span class="line"><span style="color:#F8F8F2">      case (?canisterId) {</span></span>
<span class="line"><span style="color:#F8F8F2">        let self: Principal = Principal.fromActor(Main);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        let controllers: ?[Principal] = ?[canisterId, caller, self];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        await ic.update_settings(({canister_id = canisterId; settings = {</span></span>
<span class="line"><span style="color:#F8F8F2">            controllers = controllers;</span></span>
<span class="line"><span style="color:#F8F8F2">            freezing_threshold = null;</span></span>
<span class="line"><span style="color:#F8F8F2">            memory_allocation = null;</span></span>
<span class="line"><span style="color:#F8F8F2">            compute_allocation = null;</span></span>
<span class="line"><span style="color:#F8F8F2">        }}));</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        return canisterId;</span></span>
<span class="line"><span style="color:#F8F8F2">      };</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"><span style="color:#F8F8F2">  };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  public query func getCanisterId() : async ?Principal {</span></span>
<span class="line"><span style="color:#F8F8F2">    canisterId</span></span>
<span class="line"><span style="color:#F8F8F2">  };</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><hr />
<h2>Web Application</h2>
<p>With the two canister smart contracts being implemented, we can develop a dummy frontend to test their functionalities. It can contain two actions: one to create a bucket and another to call it — i.e., to call its function <code>say</code>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">html</span><span style="color:#50FA7B;font-style:italic"> lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">en</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">body</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">main</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">button</span><span style="color:#50FA7B;font-style:italic"> id</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">init</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Init&#x3C;/</span><span style="color:#FF79C6">button</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">button</span><span style="color:#50FA7B;font-style:italic"> id</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">say</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Say&#x3C;/</span><span style="color:#FF79C6">button</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#FF79C6">main</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">body</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">html</span><span style="color:#F8F8F2">></span></span></code></pre><hr />
<p>If you ever created a sample project with the <a href="https://smartcontracts.org/docs/current/references/cli-reference/dfx-parent/" rel="noopener noreferrer external">dfx</a> command line, following will feel familiar.</p>
<p>I created a project named <code>buckets\_sample</code>. Dfx automatically installs the dependencies and a function that exposes the <code>main</code> actor. Therefore the JavaScript function that calls the manager to instantiate a new canister uses these pre-made methods. I also save the bucket — the principal ID of the last canister that is created — in a global variable for reuse purpose.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { buckets_sample } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">../../declarations/buckets_sample</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">let</span><span style="color:#F8F8F2"> bucket;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> initCanister</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        bucket </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> buckets_sample.</span><span style="color:#50FA7B">init</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">        console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">New bucket:</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, bucket.</span><span style="color:#50FA7B">toText</span><span style="color:#F8F8F2">());</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#F8F8F2">        console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(err);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> init</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> btnInit </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> document.</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">button#init</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    btnInit.</span><span style="color:#50FA7B">addEventListener</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">click</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, initCanister);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">document.</span><span style="color:#50FA7B">addEventListener</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">DOMContentLoaded</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, init);</span></span></code></pre><hr />
<p>On the contrary, the process that creates a new sample project is not aware that we want to dynamically create canisters. That is why we have to generate the candid interfaces and related JavaScript code for the bucket we have coded previously.</p>
<p><a href="https://forum.dfinity.org/t/how-do-i-upgrade-child-canisters-that-were-dynamically-created-from-a-parent-canister-of-which-i-am-the-controlller-in-motoko/12289/27?u=peterparker" rel="noopener noreferrer external">Currently</a> there is no other way to generate these files than the following workaround:</p>
<p>1. Edit the configuration <code>dfx.json</code> to list the bucket actor.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#E9F284">"</span><span style="color:#F1FA8C">canisters</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">: {</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">buckets_sample</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FE">      "</span><span style="color:#8BE9FD">main</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">src/buckets_sample/main.mo</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">      "</span><span style="color:#8BE9FD">type</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">motoko</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    },</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">bucket</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">&#x3C;-----</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">add</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">an</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">entry</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">for</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">the</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">bucket</span></span>
<span class="line"><span style="color:#8BE9FE">      "</span><span style="color:#8BE9FD">main</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">src/buckets_sample/bucket.mo</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">      "</span><span style="color:#8BE9FD">type</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">motoko</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    },</span></span></code></pre><p>2. Run the <code>dfx deploy</code> command to generate the files. The command will end in error (&quot;Error: Invalid data: Expected arguments but found none.&quot;) that can safely be ignored 😉.</p>
<p>3. Revert the change in <code>dfx.json</code>.</p>
<p>4. Copy the generated files to the source folder so that we can use them in the web application.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">rsync</span><span style="color:#BD93F9"> -av</span><span style="color:#F1FA8C"> .dfx/local/canisters/bucket</span><span style="color:#F1FA8C"> ./src/declarations</span><span style="color:#BD93F9"> --exclude=bucket.wasm</span></span></code></pre><p>A bit of mumbo jumbo but that does the trick 😁.</p>
<p>Thanks the newly generated declaration files, we can create a custom function that instantiate an actor for the bucket — canister ID — we generate on the fly.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Actor, HttpAgent } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@dfinity/agent</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { idlFactory } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">../../declarations/bucket</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> createBucketActor</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">canisterId</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> agent </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> HttpAgent</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (process.env.</span><span style="color:#BD93F9">NODE_ENV</span><span style="color:#FF79C6"> !==</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">production</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        await</span><span style="color:#F8F8F2"> agent.</span><span style="color:#50FA7B">fetchRootKey</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> Actor.</span><span style="color:#50FA7B">createActor</span><span style="color:#F8F8F2">(idlFactory, {</span></span>
<span class="line"><span style="color:#F8F8F2">        agent,</span></span>
<span class="line"><span style="color:#F8F8F2">        canisterId</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Note in above snippet how I explicitly <code>import</code> another <code>idlFactory</code>, the one that matches the definition of the bucket.</p>
<p>We can ultimately implement the code that calls the <code>say</code> function, which also ends the development of the demo application.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Actor, HttpAgent } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@dfinity/agent</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { buckets_sample } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">../../declarations/buckets_sample</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { idlFactory } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">../../declarations/bucket</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> createBucketActor</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">canisterId</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> agent </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> HttpAgent</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (process.env.</span><span style="color:#BD93F9">NODE_ENV</span><span style="color:#FF79C6"> !==</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">production</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        await</span><span style="color:#F8F8F2"> agent.</span><span style="color:#50FA7B">fetchRootKey</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> Actor.</span><span style="color:#50FA7B">createActor</span><span style="color:#F8F8F2">(idlFactory, {</span></span>
<span class="line"><span style="color:#F8F8F2">        agent,</span></span>
<span class="line"><span style="color:#F8F8F2">        canisterId</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">let</span><span style="color:#F8F8F2"> bucket;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> initCanister</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        bucket </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> buckets_sample.</span><span style="color:#50FA7B">init</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">        console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">New bucket:</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, bucket.</span><span style="color:#50FA7B">toText</span><span style="color:#F8F8F2">());</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#F8F8F2">        console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(err);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> sayHello</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> actor </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> createBucketActor</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">            canisterId</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> bucket</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"><span style="color:#F8F8F2">        console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">await</span><span style="color:#F8F8F2"> actor.</span><span style="color:#50FA7B">say</span><span style="color:#F8F8F2">());</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#F8F8F2">        console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(err);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> init</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> btnInit </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> document.</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">button#init</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    btnInit.</span><span style="color:#50FA7B">addEventListener</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">click</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, initCanister);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> btnSay </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> document.</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">button#say</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    btnSay.</span><span style="color:#50FA7B">addEventListener</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">click</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, sayHello);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">document.</span><span style="color:#50FA7B">addEventListener</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">DOMContentLoaded</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, init);</span></span></code></pre><hr />
<h2>Demo</h2>
<p>Everything comes to an end, the sample project can finally be tested 😉.</p>
<p>The <code>init</code> button dynamically creates a new canister, parse its ID in the console and the <code>say</code> button calls the function of the new bucket.</p>
<p><img src="https://6zvwc-sqaaa-aaaal-aalma-cai.raw.ic0.app/images/ezgif.com-gif-maker(1).gif?token=KxVe2t40yk3ZCguV3_-B1" alt="ezgif.com-gif-maker(1).gif" / loading="lazy"></p>
<hr />
<h2>Conclusion</h2>
<p>It took me much longer than expected to write this article 😅. I hope it will be useful, and I am looking forward to share more tricks I learned while developing <a href="https://papy.rs" rel="noopener noreferrer external">Papyrs</a>.</p>
<p>Speaking of which, if you have any related questions or suggestions that would made interesting blog posts, reach out and let me know!?!</p>
<p>To infinity and beyond,<br />
David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[A Frameworkless Store In TypeScript]]></title>
          <description><![CDATA[Have you ever developed applications that have their API, services and state management fully separated from their core?]]></description>
          <link>https://daviddalbusco.com/blog/a-frameworkless-store-in-typescript/</link>
          <pubDate>Mon, 02 May 2022 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://images.unsplash.com/photo-1508004680771-708b02aabdc0?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwzMDkyMzV8MHwxfHNlYXJjaHwyfHxmcmFtZXxlbnwwfHx8fDE2NTA3MTEwOTQ&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=1080" alt="San Francisco 1.4" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@pinewatt?utm_source=Papyrs&amp;utm_medium=referral" rel="noopener noreferrer external">pine watt</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<hr />
<p>There are undeniable advantages to using frameworks (React, Angular, Svelte etc.) for frontend development but there are also undeniable disadvantages.</p>
<p>The interoperability and compatibility between projects across various technologies is often an issue that has to be anticipated for long-lasting applications and for corporates that share resources among teams.</p>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components" rel="noopener noreferrer external">Web Components</a> - a suite of different technologies allowing you to create reusable agnostic custom elements - is a common key to the challenge. However, their usage is often limited to the creation of design systems or rich UI components.</p>
<p>According my experience, few companies explicitly enforce the separation of the presentation layers from the business layers in their frontend apps.</p>
<p>In all honesty, have you often developed applications that have the API and services layers fully separated from your projects? Did you ever extracted the state management of your applications to reusable libraries?</p>
<p>This is what I was looking to solve when I developed <a href="https://papy.rs" rel="noopener noreferrer external">Papyrs</a> - an open-source, privacy-first, decentralized blogging platform that lives 100% on chain.</p>
<hr />
<h2>Abstract</h2>
<p>In this blog post I present an architecture that separate business and state management logic for two applications developed with two different technologies (<a href="https://kit.svelte.dev/" rel="noopener noreferrer external">Sveltekit</a> and <a href="https://stenciljs.com/" rel="noopener noreferrer external">Stencil</a>).</p>
<p>After what I display the effective frameworkless code of the store I developed in TypeScript and how it can be integrated in these two apps.</p>
<hr />
<h2>Architecture</h2>
<p>Papyrs is a spin-off project of <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>. Both are web editors that should save and publish data on <a href="https://dfinity.org/" rel="noopener noreferrer external">DFINITY</a>'s Internet Computer - i.e. their goals are different but their architectures should be the same.</p>
<p>Because DeckDeckGo was migrated last year - not yet live - to the Internet Computer while remaining backwards compatible with <a href="https://firebase.google.com" rel="noopener noreferrer external">Google Firebase</a>, it was already designed to support various providers. The API layer was already implemented in distinctive libraries.</p>
<p>However, when I started the implementation of Papyrs, the business logic and the state management were still implemented within the DeckDeckGo's application.</p>
<p>I had therefore to ask myself if I was eager to reimplement these features from scratch (😅) or if any other solution - such as reusing and sharing libraries - would be a solid option.</p>
<p><img src="https://6zvwc-sqaaa-aaaal-aalma-cai.raw.ic0.app/images/untitled-2022-02-07-1330(2).png?token=dzH5gUzX0QgiPJ2wS4p3s" alt="untitled-2022-02-07-1330(2).png" / loading="lazy"></p>
<p>As you reading this article, I assume you know what was the outcome: I have extracted and separated the features to reuse the exact same code in the two projects 😄.</p>
<p>While extracting the business logic was a relatively straight forward operation - mostly stateless functions - extracting the store was a bit more tricky.</p>
<p>Fortunately I ultimately found a solution - the one I share in next chapters - which mixes an agnostic writable state management and readable stores.</p>
<p>Basically, the stores of the library take care of holding the states while the applications replicate these values to update the UI.</p>
<p><img src="https://6zvwc-sqaaa-aaaal-aalma-cai.raw.ic0.app/images/untitled-2022-02-07-1330(3).png?token=Af2JN1SZulvWHW28hVmn7" alt="untitled-2022-02-07-1330(3).png" / loading="lazy"></p>
<hr />
<h2>Frameworkless code</h2>
<p>The store is nothing less than a generic <code>class</code> which contains a value and exposes <code>set</code> and <code>get</code> functions.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> Store</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">    constructor</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">private</span><span style="color:#FFB86C;font-style:italic"> value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> T</span><span style="color:#F8F8F2">) {}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    set</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> T</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        this</span><span style="color:#F8F8F2">.value </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> value;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    get</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> T</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.value;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>The store should also propagate the changes - a consumer should be made aware when a value is modified. Therefore it should provide a way to register callbacks.</p>
<p>In addition, when a consumer register a listener, it should also be able to stop listening for changes - i.e. to unregister the callback.</p>
<p>That's why the store assign a unique ID to each callback that gets registered. Thanks to these identifier, it is possible to return a function that can unsubscribe the listener.</p>
<p>To generate unique identifier I use <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol" rel="noopener noreferrer external">Symbol</a> - a browsers' built-in object whose constructor returns a <code>symbol</code> that's guaranteed to be unique.</p>
<p>The <code>subscribe</code> function also calls the callback that gets registered. That way, the consumer receives instantly the current value without having to wait for the next update.<br />
Finally, the setter <code>propagate</code> the new value to all callbacks that are registered.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">interface</span><span style="color:#8BE9FD;font-style:italic"> StoreCallback</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#F8F8F2">    id</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Symbol</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#50FA7B">    callback</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">data</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> T</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#8BE9FD;font-style:italic"> void</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> Store</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">    private</span><span style="color:#F8F8F2"> callbacks</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> StoreCallback</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">>[] </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> [];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    constructor</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">private</span><span style="color:#FFB86C;font-style:italic"> value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> T</span><span style="color:#F8F8F2">) {}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    set</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> T</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        this</span><span style="color:#F8F8F2">.value </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> value;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">propagate</span><span style="color:#F8F8F2">(value);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    get</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> T</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.value;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    private</span><span style="color:#50FA7B"> propagate</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">data</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> T</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        this</span><span style="color:#F8F8F2">.callbacks.</span><span style="color:#50FA7B">forEach</span><span style="color:#F8F8F2">(({ </span><span style="color:#FFB86C;font-style:italic">callback</span><span style="color:#F8F8F2"> }</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> StoreCallback</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">>) </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> callback</span><span style="color:#F8F8F2">(data));</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    subscribe</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">callback</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">data</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> T</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#8BE9FD;font-style:italic"> void</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#8BE9FD;font-style:italic"> void</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> callbackId</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Symbol</span><span style="color:#FF79C6"> =</span><span style="color:#50FA7B"> Symbol</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        this</span><span style="color:#F8F8F2">.callbacks.</span><span style="color:#50FA7B">push</span><span style="color:#F8F8F2">({ id</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> callbackId, callback });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">        callback</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.value);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span></span>
<span class="line"><span style="color:#F8F8F2">            (</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.callbacks </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.callbacks.</span><span style="color:#50FA7B">filter</span><span style="color:#F8F8F2">(({ </span><span style="color:#FFB86C;font-style:italic">id</span><span style="color:#F8F8F2"> }</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> StoreCallback</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">>) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> id </span><span style="color:#FF79C6">!==</span><span style="color:#F8F8F2"> callbackId));</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h2>Demo</h2>
<p>To give a try to the above generic state management, we can create a dummy store and a consumer.</p>
<hr />
<h2>Store</h2>
<p>Following store contains an object <code>Doc</code> initialized with <code>null</code>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Store } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./store</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> interface</span><span style="color:#8BE9FD;font-style:italic"> Doc</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    title</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> docStore</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Store</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Doc</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Store</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Doc</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#F8F8F2">>(</span><span style="color:#BD93F9">null</span><span style="color:#F8F8F2">);</span></span></code></pre><hr />
<h3>Consumer</h3>
<p>The consumer - a test script - creates two subscribers, set a first value &quot;hello&quot;, unsubscribe the first listener and set a new value &quot;world&quot;.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { docStore, </span><span style="color:#FF79C6">type</span><span style="color:#F8F8F2"> Doc } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./doc.store</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> print</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">subscriber</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">value</span><span style="color:#F8F8F2"> }</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { subscriber</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">; value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Doc</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">subscriber</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">:`</span><span style="color:#F8F8F2">, value);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> unsubscribe1 </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> docStore.</span><span style="color:#50FA7B">subscribe</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Doc</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span></span>
<span class="line"><span style="color:#50FA7B">    print</span><span style="color:#F8F8F2">({ subscriber</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Subscribe #1</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, value })</span></span>
<span class="line"><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> unsubscribe2 </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> docStore.</span><span style="color:#50FA7B">subscribe</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Doc</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span></span>
<span class="line"><span style="color:#50FA7B">    print</span><span style="color:#F8F8F2">({ subscriber</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Subscribe #2</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, value })</span></span>
<span class="line"><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">docStore.</span><span style="color:#50FA7B">set</span><span style="color:#F8F8F2">({ title</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">hello</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> });</span></span>
<span class="line"><span style="color:#F8F8F2">console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Get:</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, docStore.</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">());</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">unsubscribe1</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">docStore.</span><span style="color:#50FA7B">set</span><span style="color:#F8F8F2">({ title</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">world</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> });</span></span>
<span class="line"><span style="color:#F8F8F2">console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Get:</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, docStore.</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">());</span></span></code></pre><hr />
<h3>Test</h3>
<p>If we run the above script in a terminal we shall notice the following:</p>
<ul>
<li>the subscribers get the current value instantly</li>
<li>the first subscriber is successfully unregistered</li>
<li>the store retains the value and triggers the subscribers that are registered</li>
</ul>
<p>We can conclude everything works as expected 🥳.</p>
<p><img src="https://6zvwc-sqaaa-aaaal-aalma-cai.raw.ic0.app/images/capture-d%E2%80%99e%CC%81cran-2022-04-28-a%CC%80-09.33.11.png?token=XOC8HnN1mH9s5yNOX64l6" alt="capture-d%E2%80%99e%CC%81cran-2022-04-28-a%CC%80-09.33.11.png" / loading="lazy"></p>
<hr />
<h2>Packaging</h2>
<p>I was seeking to fully isolate the business logic from my applications. If the stores would have been made fully accessible by the libraries that contains them, the applications would have been able to write values.<br />
To solve this requirement, my libraries only expose the interfaces and subscribers of the stores.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#F8F8F2"> { </span><span style="color:#FF79C6">type</span><span style="color:#F8F8F2"> Doc } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./doc.store</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> docSubscribe</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#50FA7B">callback</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">doc</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Doc</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#8BE9FD;font-style:italic"> void</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> (() </span><span style="color:#FF79C6">=></span><span style="color:#8BE9FD;font-style:italic"> void</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span></span>
<span class="line"><span style="color:#F8F8F2">    docStore.</span><span style="color:#50FA7B">subscribe</span><span style="color:#F8F8F2">(callback);</span></span></code></pre><hr />
<h2>Usage</h2>
<p>The above solution is agnostic. It is written in TypeScript and is compiled to JavaScript. That is why it can be integrated in any modern frameworks or even without 😁.</p>
<hr />
<h3>Svelte</h3>
<p><a href="https://svelte.dev/tutorial/readable-stores" rel="noopener noreferrer external">Readable stores</a> is an interesting pattern that is provided by Svelte out of the box. The store of the library introduced in previous chapters can be scoped within a function that can write to a store of the application but that cannot be called anywhere else.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { docSubscribe, </span><span style="color:#FF79C6">type</span><span style="color:#F8F8F2"> Doc } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">state-mgmt</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { readable } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">svelte/store</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> start</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#50FA7B">set</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Doc</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#8BE9FD;font-style:italic"> void</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#50FA7B"> unsubscriber</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#8BE9FD;font-style:italic"> void</span><span style="color:#FF79C6"> =</span><span style="color:#50FA7B"> docSubscribe</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">doc</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Doc</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> set</span><span style="color:#F8F8F2">(doc));</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> stop</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#50FA7B">        unsubscriber</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> doc </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> readable</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Doc</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#F8F8F2">>(</span><span style="color:#BD93F9">null</span><span style="color:#F8F8F2">, start);</span></span></code></pre><p>Each time the library set a value in the state management, the subscriber is called and the current value is replicated to the Svelte store. Ultimately the UI is updated.</p>
<hr />
<h3>Stencil</h3>
<p>The <a href="https://github.com/ionic-team/stencil-store" rel="noopener noreferrer external">@stencil/store</a> out of the box is writable. Fortunately - thanks to a solution shared by Philipp Mudra on the Stencil's slack channel - it can be made readonly with a TypeScript <a href="https://www.typescriptlang.org/docs/handbook/utility-types.html#readonlytype" rel="noopener noreferrer external">utility</a>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { createStore } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@stencil/store</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { docSubscribe, </span><span style="color:#FF79C6">type</span><span style="color:#F8F8F2"> Doc } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">state-mgmt</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">interface</span><span style="color:#8BE9FD;font-style:italic"> DocStore</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    doc</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Doc</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> { state } </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> createStore</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">DocStore</span><span style="color:#F8F8F2">>({ doc</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> null</span><span style="color:#F8F8F2"> });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">docSubscribe</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">doc</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Doc</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    state.doc </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> doc;</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> readonlyState</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Readonly</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">typeof</span><span style="color:#F8F8F2"> state> </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> state;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> { state</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> readonlyState };</span></span></code></pre><hr />
<h2>Conclusion</h2>
<p>In my opinion, it is kind of cool to have the all business logic and state management fully disconnected from the application.</p>
<p>If one of these days I wanted to develop a new UI for <a href="https://papy.rs" rel="noopener noreferrer external">Papyrs</a>, I would be able to do so quickly and without worrying about any logic.</p>
<p>In addition, the solution is scalable. I can reuse and replicate the same approach over and over again until I have got no more idea of new applications to develop on web3 😜.</p>
<p>To infinity and beyond<br />
David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Panoramaweg - Ägerital-Sattel]]></title>
          <link>https://daviddalbusco.com/trails/2022/panoramaweg-agerital-sattel/</link>
          <pubDate>Sun, 01 May 2022 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>Panoramaweg around the Ägerisee (or Lake Aegeri).</p>
<p>Moving time 2h56.</p>
<p>From Bus stop &quot;Unterägeri - Center&quot;, follow the yellow signs &quot;Panoramaweg&quot; all the way long.</p>
<p>Some segments are on asphalt.</p>
<p>Nice view, climb not too steep and few people. It was a lovely run.</p>
<p>More info: https://www.aegerital-sattel.ch/erleben/wandern/Panoramaweg</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Call Internet Computer Canister Smart Contracts In NodeJS]]></title>
          <description><![CDATA[How to query canister smart contracts on the Internet Computer in a NodeJS context.]]></description>
          <link>https://daviddalbusco.com/blog/call-internet-computer-canister-smart-contracts-in-nodejs/</link>
          <pubDate>Fri, 22 Apr 2022 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://images.unsplash.com/photo-1444703686981-a3abbc4d4fe3?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwzMDkyMzV8MHwxfHNlYXJjaHw1fHx3ZWJ8ZW58MHx8fHwxNjUwNTQ3NDAz&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=1080" alt="https://unsplash.com/photos/oMpAz-DN-9I" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@grakozy?utm_source=Papyrs&amp;utm_medium=referral" rel="noopener noreferrer external">Greg Rakozy</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<hr />
<p>I spent last few months developing <a href="https://papy.rs/" rel="noopener noreferrer external">Papyrs</a> an open-source, privacy-first, decentralized blogging platform that lives 100% on chain. This new web editor is finally ready for testing, I can write some blog posts again 😁.</p>
<p>This new web3 platform uses <a href="https://dfinity.org/" rel="noopener noreferrer external">DFINITY's</a> Internet Computer. Because each registered user gets two smart contracts, it was particularly useful that I develop scripts to administrate these canisters - e.g. querying remaining <a href="https://smartcontracts.org/docs/developers-guide/concepts/tokens-cycles.html" rel="noopener noreferrer external">cycles</a> or updating code.</p>
<p>As a frontend developer, I am more familiar with <a href="https://nodejs.org/en/" rel="noopener noreferrer external">NodeJS</a> than any other scripting languages. That's why I used this engine to implement my tools.</p>
<hr />
<h2>Getting Started</h2>
<p>Calling the default <code>greet(name: Text)</code> query function that is generated by <code>dfx new &lt;PROJECT\_NAME&gt;</code> might be an interesting example.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">actor {</span></span>
<span class="line"><span style="color:#F8F8F2">  public func </span><span style="color:#50FA7B">greet</span><span style="color:#F8F8F2">(name : Text) : async Text {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Hello, </span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> # name # </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">!</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  };</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>That's why in following chapters, we will implement a script - let's call it <code>hello.mjs</code> - that queries this particular function in NodeJS.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#6272A4">    // TODO: implement query function</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> result </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> query</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`Result of canister call: </span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">result</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">} </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`Error while querying.`</span><span style="color:#F8F8F2">, err);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Note: if you wish to follow this post step by step, you can initialize a new sample project with <code>dfx new helloworld</code>.</p>
<p>Once created, switch directory <code>cd helloworld</code>, start a local simulated network <code>dfx start --background</code> and deploy the project <code>dfx deploy</code>.</p>
<hr />
<h2>ECMAScript modules</h2>
<p>There might be some other ways but I only managed to use both NodeJS LTS and <code>@dfinity/agent</code> libraries with <code>.mjs</code> scripts - i.e. not with common <code>.js</code> scripts.</p>
<p>That's why, the <code>candid</code> JavaScript files that are generated by the <code>dfx</code> build command - the <code>did</code> files - actually need to be converted to ECMAScript modules too.</p>
<p>Basically <code>cp helloworld.did.js hellowrold.did.mjs</code> and that is already it.</p>
<p>Someday the auto-generated files might be generated automatically as modules too but I have to admit, I did not even bother to open a feature request about it.</p>
<p>In my project, of course I automated the copy with a NodeJS script as well (🤪). If it can be useful, here's the code snippet:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { readFileSync, writeFileSync } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">fs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> copyJsToMjs</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> srcFolder </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./src/declarations/helloworld</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> buffer </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> readFileSync</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">srcFolder</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">/helloworld.did.js`</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#50FA7B">    writeFileSync</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">srcFolder</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">/helloworld.did.mjs`</span><span style="color:#F8F8F2">, buffer.</span><span style="color:#50FA7B">toString</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">utf-8</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">));</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    copyJsToMjs</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`IC types copied!`</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">} </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`Error while copying the types.`</span><span style="color:#F8F8F2">, err);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h2>Script &quot;Hello World&quot;</h2>
<p>NodeJS v18 introduces the experimental native support of the <a href="https://nodejs.org/en/blog/announcements/v18-release-announce/" rel="noopener noreferrer external">fetch command</a>. For LTS version, <a href="https://github.com/node-fetch/node-fetch" rel="noopener noreferrer external">node-fetch</a> is required.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> i</span><span style="color:#F1FA8C"> node-fetch</span><span style="color:#BD93F9"> -D</span></span></code></pre><p>No further dependencies than those provided by the template need to be installed.</p>
<p>To query the IC (Internet Computer) with use <a href="https://github.com/dfinity/agent-js" rel="noopener noreferrer external">agent-js</a>. We create an actor for the <code>candid</code> interface and we effectively call the function <code>greet('world')</code>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> query</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> actor </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> actorIC</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> actor.</span><span style="color:#50FA7B">greet</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">world</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>The initialization of the actor is very similar to the frontend code that is provided by the default template. However there is two notable differences that are needed to query the IC in a NodeJS context:</p>
<ul>
<li>a <code>host</code> has to be provided because the runtime time environment is not a browser and the code is not served by an &quot;asset&quot; canister</li>
<li><code>node-fetch</code> is required to provide <code>agent-js</code> a way to execute network requests</li>
</ul>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> fetch </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">node-fetch</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> pkgAgent </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@dfinity/agent</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> { HttpAgent, Actor } </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> pkgAgent;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { idlFactory } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./src/declarations/helloworld/helloworld.did.mjs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> actorIC</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#6272A4">    // TODO: implement actor initialization</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> canisterId </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> actorCanisterIdLocal</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> host </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">http://localhost:8000/</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">; </span><span style="color:#6272A4">// Mainnet: 'https://ic0.app'</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> agent </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> HttpAgent</span><span style="color:#F8F8F2">({ fetch, host });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // Local only</span></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#F8F8F2"> agent.</span><span style="color:#50FA7B">fetchRootKey</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> Actor.</span><span style="color:#50FA7B">createActor</span><span style="color:#F8F8F2">(idlFactory, {</span></span>
<span class="line"><span style="color:#F8F8F2">        agent,</span></span>
<span class="line"><span style="color:#F8F8F2">        canisterId</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Finally the canister ID can be retrieved. Of course we can also hardcode its value but I find it handy to read the information dynamically.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { readFileSync } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">fs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> pkgPrincipal </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@dfinity/principal</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> { Principal } </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> pkgPrincipal;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> actorCanisterIdLocal</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> buffer </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> readFileSync</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">./.dfx/local/canister_ids.json</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { helloworld } </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">parse</span><span style="color:#F8F8F2">(buffer.</span><span style="color:#50FA7B">toString</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">utf-8</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">));</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> Principal.</span><span style="color:#50FA7B">fromText</span><span style="color:#F8F8F2">(helloworld.local);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> actorCanisterIdMainnet</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> buffer </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> readFileSync</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">./canister_ids.json</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { helloworld } </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">parse</span><span style="color:#F8F8F2">(buffer.</span><span style="color:#50FA7B">toString</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">utf-8</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">));</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> Principal.</span><span style="color:#50FA7B">fromText</span><span style="color:#F8F8F2">(helloworld.ic);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>The script is implemented. Run in a terminal, it outputs the expected result <em>&quot;Hello, world!&quot;</em> 🥳.</p>
<p><img src="https://6zvwc-sqaaa-aaaal-aalma-cai.raw.ic0.app/images/capture-d%E2%80%99e%CC%81cran-2022-04-22-a%CC%80-08.37.54.png?token=3DCOCTbgqYQNdkYhdao8d" alt="capture-d%E2%80%99e%CC%81cran-2022-04-22-a%CC%80-08.37.54.png" / loading="lazy"></p>
<hr />
<h2>Conclusion</h2>
<p>Calling canisters in NodeJS is really handy notably to implement tasks that have administrative purpose. In a follow up blog post I will probably share how I enhanced this solution in order to update - install code in my users' canisters. After all, I still need to test <a href="https://papy.rs" rel="noopener noreferrer external">Papyrs</a> 😉.</p>
<p>To infinity and beyond<br />
David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Export To The File System (Save As...) + Fallback In TypeScript]]></title>
          <description><![CDATA[How to save a file to the user's local devices with the new File System Access API and a fallback for incompatible browsers.]]></description>
          <link>https://daviddalbusco.com/blog/export-to-the-file-system-save-as-fallback-in-typescript/</link>
          <pubDate>Fri, 18 Feb 2022 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://zhx6p-eqaaa-aaaai-abbrq-cai.raw.ic0.app/images/ivan-diaz-gcynzrxspce-unsplash-copie.jpg?token=7d3dab62-3d70-4541-a9f6-8057f0dda373" alt="" / loading="lazy"></p>
<p>Photo by <a href="https://unsplash.com/@ivvndiaz?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Ivan Diaz</a> on <a href="https://unsplash.com/s/photos/download?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></p>
<hr />
<p>In almost every web application I end up reusing the same pattern to export data to the file system in JavaScript - i.e. a solution that uses the <a href="https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API" rel="noopener noreferrer external">File System Access API</a> and a good old &quot;download&quot; feature as fallback. I thought it was worth writing a post about it as documentation purpose 😉.</p>
<hr />
<h2>Introduction</h2>
<p>The File System Access API allows read, write and file management capabilities within your browser. It enables developers to build powerful web apps that interact with files on the user's local device.</p>
<p>The web.dev team has a <a href="https://web.dev/file-system-access/" rel="noopener noreferrer external">tutorial</a> that introduces and highlights all the features.</p>
<p>It is a relatively new API and therefore is not yet adopted by all browser vendors.</p>
<p>One of the key feature we are about the use - <code>showSaveFilePicker</code> which opens a dialog to select the destination of the file that will be written on user's local drive - is only supported by Edge, Chrome and Opera (Feb. 2022- source <a href="https://caniuse.com/?search=showSaveFilePicker" rel="noopener noreferrer external">Caniuse</a>).</p>
<hr />
<h2>Getting Started</h2>
<p>Generally speaking I use TypeScript. This solution is provided with type safety as well. That's why it needs first the installation of the type definitions for the File System Access API.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> i</span><span style="color:#BD93F9"> -D</span><span style="color:#F1FA8C"> @types/wicg-file-system-access</span></span></code></pre><hr />
<h2>Hands-on</h2>
<p>To export file I use a <code>Blob</code> - i.e. the content of the file I want to export - and a <code>filename</code> . I create a single function that saves to the user's local device and that can be use across my app.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> save</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">data</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {blob</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Blob</span><span style="color:#F8F8F2">, filename</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">}) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> (</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">showSaveFilePicker</span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> in</span><span style="color:#F8F8F2"> window) {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#50FA7B"> exportNativeFileSystem</span><span style="color:#F8F8F2">(data);</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#50FA7B"> download</span><span style="color:#F8F8F2">(data);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><hr />
<h3>File System Access API - Save As</h3>
<p>Above feature tests if <code>showSaveFilePicker</code> is available in the <code>window</code> object - i.e. it checks if the browser supports the File System Access API or not.</p>
<p>To save the file with the new API, we first show the user a dialog in &quot;save&quot; mode. Using it, user can pick the location where the file will be saved. Once the path set, the file can effectively be written to the local drive.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> exportNativeFileSystem </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#FF79C6">      async</span><span style="color:#F8F8F2"> ({</span><span style="color:#FFB86C;font-style:italic">blob</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">filename</span><span style="color:#F8F8F2">}</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {blob</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Blob</span><span style="color:#F8F8F2">, filename</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">}) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> fileHandle</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> FileSystemFileHandle</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#FF79C6">      await</span><span style="color:#50FA7B"> getNewFileHandle</span><span style="color:#F8F8F2">({filename});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">fileHandle) {</span></span>
<span class="line"><span style="color:#FF79C6">    throw</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Error</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Cannot access filesystem</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  await</span><span style="color:#50FA7B"> writeFile</span><span style="color:#F8F8F2">({fileHandle, blob});</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>In many cases I want my app to suggest a default file name. This can be achieved by setting <code>suggestedName</code>. In addition, I also scope the type(s) of files that can be selected by providing mime types and related file extensions.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> getNewFileHandle</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">filename</span><span style="color:#F8F8F2"> }</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { filename</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2"> })</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">FileSystemFileHandle</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> opts</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> SaveFilePickerOptions</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        suggestedName</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> filename,</span></span>
<span class="line"><span style="color:#F8F8F2">        types</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#F8F8F2">            {</span></span>
<span class="line"><span style="color:#F8F8F2">                description</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Markdown file</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">                accept</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#E9F284">                    "</span><span style="color:#F1FA8C">text/plain</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">.md</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">]</span></span>
<span class="line"><span style="color:#F8F8F2">                }</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"><span style="color:#F8F8F2">        ]</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#50FA7B"> showSaveFilePicker</span><span style="color:#F8F8F2">(opts);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Finally, the file can be effectively written with <code>writeFile</code> - another function of the API. It uses the file handle I previously requested to know where to export the data on the file system.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> writeFile </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#FF79C6">    async</span><span style="color:#F8F8F2"> ({</span><span style="color:#FFB86C;font-style:italic">fileHandle</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">blob</span><span style="color:#F8F8F2">}</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {fileHandle</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> FileSystemFileHandle</span><span style="color:#F8F8F2">, blob</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Blob</span><span style="color:#F8F8F2">}) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> writer </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> fileHandle.</span><span style="color:#50FA7B">createWritable</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">  await</span><span style="color:#F8F8F2"> writer.</span><span style="color:#50FA7B">write</span><span style="color:#F8F8F2">(blob);</span></span>
<span class="line"><span style="color:#FF79C6">  await</span><span style="color:#F8F8F2"> writer.</span><span style="color:#50FA7B">close</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><hr />
<h3>Fallback - Download</h3>
<p>As a fallback, I add to the DOM a temporary anchor element that is automatically clicked. To export the file to the default download folder of the user, I provide an object as a URL for the <code>blob</code>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> download</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ({</span><span style="color:#FFB86C;font-style:italic">filename</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">blob</span><span style="color:#F8F8F2">}</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {filename</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">; blob</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Blob</span><span style="color:#F8F8F2">}) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> a</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLAnchorElement</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> document.</span><span style="color:#50FA7B">createElement</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">a</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">  a.style.display </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">none</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  document.body.</span><span style="color:#50FA7B">appendChild</span><span style="color:#F8F8F2">(a);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> url</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> window.</span><span style="color:#BD93F9">URL</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">createObjectURL</span><span style="color:#F8F8F2">(blob);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  a.href </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> url;</span></span>
<span class="line"><span style="color:#F8F8F2">  a.download </span><span style="color:#FF79C6">=</span><span style="color:#F1FA8C"> `</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">filename</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">.md`</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  a.</span><span style="color:#50FA7B">click</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  window.</span><span style="color:#BD93F9">URL</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">revokeObjectURL</span><span style="color:#F8F8F2">(url);</span></span>
<span class="line"><span style="color:#F8F8F2">  a.parentElement?.</span><span style="color:#50FA7B">removeChild</span><span style="color:#F8F8F2">(a);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><hr />
<h2>Get The Code</h2>
<p>You can find all the code presented in this article in a recent Chrome plugin I published on GitHub 👉 <a href="https://github.com/papyrs/markdown-plugin/blob/main/src/plugin/utils/save.utils.ts" rel="noopener noreferrer external">save-utils.ts</a></p>
<hr />
<h2>Summary</h2>
<p>That was a fairly short post which I hope was at least a bit entertaining 🤪. If you would like to dig deeper the File System Access API, I once again advise you to have a look at the nice post of the <a href="https://web.dev/file-system-access/" rel="noopener noreferrer external">web.dev</a> team.</p>
<p>To infinity and beyond<br />
David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Stylo - Another Kind Of Rich Text Editor]]></title>
          <description><![CDATA[So, I developed a new open source WYSIWYG interactive editor for JavaScript.]]></description>
          <link>https://daviddalbusco.com/blog/stylo-another-kind-of-rich-text-editor/</link>
          <pubDate>Wed, 09 Feb 2022 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*kPGH8RB1reGGiq6nU0UaIg.jpeg" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@amyhirschi?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Amy Hirschi</a> on <a href="https://unsplash.com/s/photos/pen?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<hr />
<p>So, I developed a new open source WYSIWYG interactive editor for JavaScript. Its goal is to bring great user experience and interactivity to the web, for everyone, with no dependencies. I called it: <a href="https://stylojs.com/" rel="noopener noreferrer external">Stylo</a>.</p>
<hr />
<h3>But Why?</h3>
<p>Last year, while migrating our web editor for slides, <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>, to <a href="https://dfinity.org" rel="noopener noreferrer external">DFINITY</a>’s Internet Computer, we started playing around with some new code to support documents as well.</p>
<p>As a very first test, I wrote and published a blog post on our platform. Although I was satisfied with the result, I thought: this can be better.</p>
<p>That’s why I had the idea to extract the rich text editor capabilities of the core of our application -- under the assumption that it would be easier to improve and fix the text editor in isolation.</p>
<p>In addition, by publishing the new library as a new open source repo, I was also hoping it would benefit from community feedback and contributions.</p>
<p>What can I add? I am an engineer, I like to code 😉.</p>
<hr />
<h3>Features</h3>
<p>Not looking to oversell what Stylo can do — or do differently — but here are in my humble opinion a list of cool things it can do:</p>
<hr />
<h4>Interactive Design 🎯</h4>
<p>Unlike other WYSIWYG editors, Stylo has no sticky toolbar. It is inspired by design and user experience of platforms that I like — i.e <a href="https://www.notion.so/" rel="noopener noreferrer external">Notion</a> and <a href="https://medium.com/" rel="noopener noreferrer external">Medium</a>.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*D-YYgIa5cY0Ya8FAN0Lyiw.gif" alt="" / loading="lazy"></p>
<p>When writing the document, the user can use different types of paragraph that can be added through a floating menu. These elements can be simple HTML elements — i.e. h1, h2, code etc. — or more complex block — i.e. any Web Component.</p>
<p>For styling purposes, another toolbar gives the user the ability to modify text — e.g. bold, italic, colors, etc. — and add hyperlinks.</p>
<p>Last but not least, for an optimized experience on desktop, many actions can be performed without the mouse. Opening the list of paragraphs can be triggered hitting “/” and <strong>bold</strong>, <em>italic</em> and <code>mark</code> are accessible through keyboard shortcuts.</p>
<hr />
<h4>Customizable 💪</h4>
<p>Conceptually, only one main element — a container — has to be set as editable and all its children are either HTML Elements or Web Components — i.e. no direct text node descendant.</p>
<p>This decision was made for performance reasons. In the future we might need to lazy load the content or improve the scrolling experience with some sort of infinite scrolling or some other virtual scrolling approach. By limiting children to elements, it is possible to assign attributes such as unique ids directly in the DOM.</p>
<p>This brings us to the customization. The various types of paragraphs that can be added by the user - e.g. block of code, titles, etc. - is fully customizable. Each type - i.e. plugin - is defined with a ​text​, an ​icon​ and a​ function that adds a new element to the document.</p>
<p>For example, if we were to implement a plugin that generates a new paragraph that is itself a Web Component name &lt;hello-world/&gt;, the solution would look something like this:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {createEmptyElement, StyloPlugin, StyloPluginCreateParagraphsParams, transformParagraph} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@papyrs/stylo</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> hr</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> StyloPlugin</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  text</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">My Hello World</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  icon</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> `&#x3C;svg width="32" height="32" viewBox="0 0 512 512"></span></span>
<span class="line"><span style="color:#F1FA8C">        ...</span></span>
<span class="line"><span style="color:#F1FA8C">    &#x3C;/svg></span></span>
<span class="line"><span style="color:#F1FA8C">  `</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#50FA7B">  createParagraphs</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ({</span><span style="color:#FFB86C;font-style:italic">container</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">paragraph</span><span style="color:#F8F8F2">}</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> StyloPluginCreateParagraphsParams</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#6272A4">    // Create your Web Component or HTML Element</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> helloWorld </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> document.</span><span style="color:#50FA7B">createElement</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">hello-world</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#6272A4">    // Set properties, attributes or styles</span></span>
<span class="line"><span style="color:#F8F8F2">    helloWorld.</span><span style="color:#50FA7B">setAttributes</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">yolo</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">true</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#50FA7B">     transformParagraph</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">       elements</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [helloWorld, </span><span style="color:#50FA7B">createEmptyElement</span><span style="color:#F8F8F2">({nodeName</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">div</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">})],</span></span>
<span class="line"><span style="color:#F8F8F2">       paragraph,</span></span>
<span class="line"><span style="color:#F8F8F2">       container,</span></span>
<span class="line"><span style="color:#F8F8F2">       focus</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">first</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">     })</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Stylo exposes interfaces and utilities to ease the development of custom plugins. The most important being maybe <code>transformParagraph</code> which adds the elements to the DOM.</p>
<p>Finally, it is worth noting that the function <code>createParagraphs</code> is async which means the process of creating new elements can totally be hijacked by your application. As for example, when integrated in DeckDeckGo, the function does not always directly create new elements. For some more complex use cases, when the function is triggered, it opens modals that require more user interactions such as selecting an image before effectively modifying the content.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*9GgkGbx_4D-hIwT8PxJivQ.gif" alt="" / loading="lazy"></p>
<hr />
<h4>Framework Agnostic 😎</h4>
<p>Stylo is a framework agnostic library of reusable components developed with <a href="https://stenciljs.com/" rel="noopener noreferrer external">StencilJS</a>. It works on all modern browsers.</p>
<hr />
<h4>Lightweight 🪶</h4>
<p>30kb gzipped might in certain circumstances already sound like a lot, especially to me, but in terms of WYSIWYG editor it actually turns out be lightweight.</p>
<p>From what I observed, it might even be the lightest. There is also probably room for improvement.</p>
<hr />
<h4>Future Proof 🚀</h4>
<p>While a handful of actions in the alpha version still rely on <code>execCommand</code> to apply styles (e.g. bold, italic) — the core of the library does not.</p>
<p>Stylo controls what happens on every mutation. It uses the MutationObserver API to keep track of the changes and stacks them in a custom “undo-redo” implementation. It also forwards the information about what changed in the DOM to your application with custom events.</p>
<p>Plugins are applied using plain vanilla JavaScript and the Selection API is also used to detect and apply transformations.</p>
<p>That's why, regardless of what happens to the deprecated API, Stylo is built for the future.</p>
<hr />
<h4>Open Source ⭐️</h4>
<p>The components are licensed under MIT license. They can be used by anyone, anywhere where the web is used and this even in a closed source application.</p>
<p>To contribute and follow the adventure, watch and star the <a href="https://github.com/papyrs/stylo" rel="noopener noreferrer external">GitHub repo</a>.</p>
<hr />
<h3>Is It Production Ready?</h3>
<p><img src="https://daviddalbusco.com/assets/images/1*snbd7Pn0GcdqaEa9m48cIQ.gif" alt="" / loading="lazy"></p>
<p>Stylo is currently an <strong>alpha</strong> version! ⚠️ The project is under active development and contributions on GitHub are most welcome.</p>
<p>I did use it — not without noticing many bugs I need to fix — to write this article. It might probably not be ready tomorrow nor the day after but I am confident that it will be ready to start the beta phase of our project that will run on the Internet Computer.</p>
<hr />
<h3>Conclusion</h3>
<p>I am well aware there is still quite some work to make <a href="https://stylojs.com" rel="noopener noreferrer external">Stylo</a> bulletproof but it is quite exciting because it is also challenging 😉.</p>
<p>To infinity and beyond!<br />
David</p>
<hr />
<h3>Further Reading</h3>
<p>Wanna read more about our project? We are porting <a href="https://deckdeckgo.com/" rel="noopener noreferrer external">DeckDeckGo</a> to <a href="https://dfinity.org/" rel="noopener noreferrer external">DFINITY’s</a> Internet Computer. Here is the list of blog posts I published since we started the project:</p>
<ul>
<li><a href="https://daviddalbusco.com/blog/converting-svg-to-image-png-webp-etc-in-javascript" rel="noopener noreferrer external">Converting SVG To Image (PNG, Webp, etc.) in JavaScript</a></li>
<li><a href="https://daviddalbusco.com/blog/a-simple-keyval-store-implemented-in-motoko" rel="noopener noreferrer external">A Simple KeyVal Store Implemented in Motoko</a></li>
<li><a href="https://daviddalbusco.com/blog/typescript-utilities-for-candid" rel="noopener noreferrer external">TypeScript Utilities For Candid</a></li>
<li><a href="https://daviddalbusco.com/blog/bye-bye-amazon-and-google-hello-web-3-0" rel="noopener noreferrer external">Bye-Bye Amazon &amp; Google, Hello Web 3.0</a></li>
<li><a href="https://daviddalbusco.com/blog/dynamically-import-esm-modules-from-a-cdn" rel="noopener noreferrer external">Dynamically Import ESM Modules From A CDN</a></li>
<li><a href="https://daviddalbusco.com/blog/internet-computer-web-app-decentralized-database-architecture" rel="noopener noreferrer external">Internet Computer: Web App Decentralized Database Architecture</a></li>
<li><a href="https://daviddalbusco.com/blog/singleton-and-factory-patterns-with-typescript" rel="noopener noreferrer external">Singleton &amp; Factory Patterns With TypeScript</a></li>
<li><a href="https://daviddalbusco.com/blog/getting-started-with-the-internet-computer-web-hosting" rel="noopener noreferrer external">Hosting on the Internet Computer</a></li>
<li><a href="https://daviddalbusco.com/blog/we-received-a-grant-to-port-our-web-app-to-the-internet-computer" rel="noopener noreferrer external">We Received A Grant To Port Our Web App To The Internet Computer</a></li>
</ul>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Converting SVG To Image (PNG, Webp, etc.) in JavaScript]]></title>
          <description><![CDATA[How to convert SVG to image in the browser without dependencies.]]></description>
          <link>https://daviddalbusco.com/blog/converting-svg-to-image-png-webp-etc-in-javascript/</link>
          <pubDate>Thu, 23 Dec 2021 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*zXDaYiF8SRHlI_OIGZnf8w.jpeg" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@justnjames?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Justin Aikin</a> on <a href="https://unsplash.com/s/photos/dog?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<hr />
<p>Last Monday I built and published <a href="https://docs.deckdeckgo.com/?path=/story/components-social-image--social-image" rel="noopener noreferrer external">a new Web Component</a>, developed with <a href="https://stenciljs.com/" rel="noopener noreferrer external">StencilJS</a>, to generate social images dynamically in the browser.</p>
<p>For a given text and logo, the component renders a shadowed SVG that can be converted to an image (PNG, Webp, etc.), and does so without any third party dependencies.</p>
<p>This is how it works.</p>
<blockquote>
<p>Note: Code snippets are written in TypeScript.</p>
</blockquote>
<hr />
<h3>SVG</h3>
<p>To create the dynamic SVG, I used a <code>foreignObject</code> to embed the text with an HTML paragraph (<code>&lt;p/&gt;</code>) and a graphical <code>image</code> element.</p>
<hr />
<h4>ForeignObject</h4>
<p>I could have used <code>&lt;text/&gt;</code> elements to draw graphics instead of HTML elements, however the feature needs to support dynamic inputs. These can be too long and might need to be truncated and displayed with three ending dots <code>...</code> .</p>
<p>This is something I found easier to implement with CSS rather than with JavaScript. <code>&lt;text/&gt;</code> are not paragraphs but lines.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">svg</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#FF79C6">    {</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.text </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">foreignObject</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span><span style="color:#FF79C6">{</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.text</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">foreignObject</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    )</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">svg</span><span style="color:#F8F8F2">></span></span></code></pre><p>The <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-line-clamp" rel="noopener noreferrer external">-webkit-line-clamp</a> CSS property allows shrinking the content of block containers to the specified number of lines.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">p {</span></span>
<span class="line"><span style="color:#F8F8F2">  display: </span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">webkit</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">box;</span></span>
<span class="line"><span style="color:#FF79C6">  -</span><span style="color:#F8F8F2">webkit</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">line</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">clamp: </span><span style="color:#BD93F9">3</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">  -</span><span style="color:#F8F8F2">webkit</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">box</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">orient: vertical;</span></span>
<span class="line"><span style="color:#F8F8F2">  overflow: hidden;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h4>Image</h4>
<p>Unlike the text, to embed the image, I had to use a graphical <code>&lt;image/&gt;</code> element.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">svg</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#FF79C6">    {</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.imgBase64 </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.imgMimeType </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">image</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            x</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">500</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            y</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">1000</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            width</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">64</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            height</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">64</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            href</span><span style="color:#FF79C6">={</span><span style="color:#F1FA8C">`data:</span><span style="color:#FF79C6">${</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.imgMimeType</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">;base64,</span><span style="color:#FF79C6">${</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.imgBase64</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#F8F8F2">        /></span></span>
<span class="line"><span style="color:#F8F8F2">    )</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">svg</span><span style="color:#F8F8F2">></span></span></code></pre><p>Using a <code>foreignObject</code> with an HTML element <code>&lt;img/&gt;</code> would have been possible for rendering purposes but, I did not find a way to ultimately export it to the resulting image.</p>
<p>For the same reason, I was also not able to render the image directly from a URL (<code>href=&quot;https://...&quot;</code>) and had to first transform it to a <code>base64</code> string.</p>
<p>Only this way the image is rendered and can be included in the converted image output.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> fetchImage</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ({</span><span style="color:#FFB86C;font-style:italic">imgSrc</span><span style="color:#F8F8F2">}</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {imgSrc</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">})</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> data</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Response</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> fetch</span><span style="color:#F8F8F2">(imgSrc);</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> blob</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Blob</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> data.</span><span style="color:#50FA7B">blob</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> base64</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> toBase64</span><span style="color:#F8F8F2">({blob});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#F8F8F2"> base64.</span><span style="color:#50FA7B">split</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">,</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">)?.[</span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">];</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> toBase64</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ({</span><span style="color:#FFB86C;font-style:italic">blob</span><span style="color:#F8F8F2">}</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {blob</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Blob</span><span style="color:#F8F8F2">})</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">>((</span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">reject</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">      const</span><span style="color:#F8F8F2"> reader</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> FileReader</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> FileReader</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">      reader.</span><span style="color:#50FA7B">onloadend</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> {result} </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> reader;</span></span>
<span class="line"><span style="color:#50FA7B">        resolve</span><span style="color:#F8F8F2">(result </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">      };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">      reader.</span><span style="color:#50FA7B">readAsDataURL</span><span style="color:#F8F8F2">(blob);</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#50FA7B">      reject</span><span style="color:#F8F8F2">(err);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>In the above code snippet, the <code>imgSrc</code> is the URL to the image -- the logo -- that should be embedded. It is first fetched, then transformed to a <code>blob</code> and finally converted to a <code>base64</code> string.</p>
<hr />
<h3>Convert To Image</h3>
<p>Basically, the conversion process happens in two steps:</p>
<ul>
<li>SVG to Canvas</li>
<li>Canvas to Image (Blob)</li>
</ul>
<p>Translated to code, these steps can be chained in a function.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Method</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">async </span><span style="color:#50FA7B">toBlob</span><span style="color:#F8F8F2">(type: string </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">image/webp</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">): </span><span style="color:#8BE9FD;font-style:italic">Promise</span><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2">Blob</span><span style="color:#FF79C6">></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  const canvas</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> HTMLCanvasElement </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#FF79C6">        await</span><span style="color:#50FA7B"> svgToCanvas</span><span style="color:#F8F8F2">({svg</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.svgRef});</span></span>
<span class="line"><span style="color:#F8F8F2">  return </span><span style="color:#50FA7B">canvasToBlob</span><span style="color:#F8F8F2">({canvas, type});</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>As you may notice, the above method defines a default mime type (<code>image/webp</code>) for the export. According to my tests, it also works for other format such as <code>image/png</code> and <code>image/jpg</code>.</p>
<hr />
<h4>SVG To Canvas</h4>
<p>In one of my previous works (a <a href="https://github.com/deckgo/figma-deckdeckgo-plugin" rel="noopener noreferrer external">Figma plugin</a>) I already developed a function that convert SVG to <code>HTMLCanvasElement</code>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> transformCanvas</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ({</span><span style="color:#FFB86C;font-style:italic">index</span><span style="color:#F8F8F2">}</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Frame</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">SvgToCanvas</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">SvgToCanvas</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">>((</span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> svg</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> SVGGraphicsElement</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#F8F8F2">      document.</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`div[frame="</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">index</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">"] svg`</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">svg) {</span></span>
<span class="line"><span style="color:#50FA7B">      resolve</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">undefined</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">      return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> {width, height} </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> svgSize</span><span style="color:#F8F8F2">(svg);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> blob</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Blob</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#FF79C6;font-weight:bold">      new</span><span style="color:#50FA7B"> Blob</span><span style="color:#F8F8F2">([svg.outerHTML],</span></span>
<span class="line"><span style="color:#F8F8F2">              {type</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">image/svg+xml;charset=utf-8</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">});</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> blobURL</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#BD93F9"> URL</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">createObjectURL</span><span style="color:#F8F8F2">(blob);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> image </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Image</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    image.</span><span style="color:#50FA7B">onload</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">      const</span><span style="color:#F8F8F2"> canvas</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLCanvasElement</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#F8F8F2">                    document.</span><span style="color:#50FA7B">createElement</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">canvas</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">      canvas.width </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> width;</span></span>
<span class="line"><span style="color:#F8F8F2">      canvas.height </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> height;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">      const</span><span style="color:#F8F8F2"> context</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> CanvasRenderingContext2D</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#F8F8F2">        canvas.</span><span style="color:#50FA7B">getContext</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">2d</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">      context?.</span><span style="color:#50FA7B">drawImage</span><span style="color:#F8F8F2">(image, </span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">, width, height);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#BD93F9">      URL</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">revokeObjectURL</span><span style="color:#F8F8F2">(blobURL);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">      resolve</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        canvas,</span></span>
<span class="line"><span style="color:#F8F8F2">        index</span></span>
<span class="line"><span style="color:#F8F8F2">      });</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    image.src </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> blobURL;</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>At first, I had the feeling it would be a piece of cake to re-implement the exact same above function. Unfortunately, “feeling” was the only valid keyword in that sentence 😂.</p>
<p>The first issue I faced was related to the conversion of the SVG to Blob. In the previous method, it converts it using the SVG value and an object URL.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> blob</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Blob</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Blob</span><span style="color:#F8F8F2">([svg.outerHTML],</span></span>
<span class="line"><span style="color:#F8F8F2">                       {type</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">image/svg+xml;charset=utf-8</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">});</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> blobURL</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#BD93F9"> URL</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">createObjectURL</span><span style="color:#F8F8F2">(blob);</span></span></code></pre><p>However, in my component, using that approach threw an exception at runtime.</p>
<blockquote>
<p>Security Error: Tainted canvases may not be exported.</p>
</blockquote>
<p>I had no other choice than finding another way to instantiate the <code>Image</code> object which, fortunately, was possible by using another serialization method.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> base64SVG</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#F8F8F2">  window.</span><span style="color:#50FA7B">btoa</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6;font-weight:bold">new</span><span style="color:#50FA7B"> XMLSerializer</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">serializeToString</span><span style="color:#F8F8F2">(svg));</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> imgSrc</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#F1FA8C"> `data:image/svg+xml;base64,</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">base64SVG</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">;</span></span></code></pre><p>Unfortunately, even if the function threw no compilation nor runtime error, it was not yet ready. Indeed, no text was exported in the resulting canvas.</p>
<p>After some “die and retry” research, I figured out that the <code>foreignObject</code> content needs its CSS styles to be <strong>inlined</strong> when exporting.</p>
<p>To solve this in a relatively dynamical way, I implemented another function that replicates all CSS styles of the original text element to a clone.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> inlineStyle</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ({</span><span style="color:#FFB86C;font-style:italic">clone</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">style</span><span style="color:#F8F8F2">}</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {clone</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> SVGGraphicsElement</span><span style="color:#F8F8F2">; style</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> CSSStyleDeclaration</span><span style="color:#F8F8F2">}) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> text</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLParagraphElement</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#F8F8F2">    clone.</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">foreignObject > p</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">text) {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  for</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> key </span><span style="color:#FF79C6">of</span><span style="color:#F8F8F2"> Object.</span><span style="color:#50FA7B">keys</span><span style="color:#F8F8F2">(style)) {</span></span>
<span class="line"><span style="color:#F8F8F2">    text.style.</span><span style="color:#50FA7B">setProperty</span><span style="color:#F8F8F2">(key, style[key]);</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Finally, the transform from SVG to canvas worked out.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> svgToCanvas</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ({</span><span style="color:#FFB86C;font-style:italic">svg</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">style</span><span style="color:#F8F8F2">}</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {svg</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> SVGGraphicsElement</span><span style="color:#F8F8F2">; style</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> CSSStyleDeclaration</span><span style="color:#F8F8F2">})</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">HTMLCanvasElement</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">HTMLCanvasElement</span><span style="color:#F8F8F2">>(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> {width, height} </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> svgSize</span><span style="color:#F8F8F2">(svg);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> clone</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> SVGGraphicsElement</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#F8F8F2">      svg.</span><span style="color:#50FA7B">cloneNode</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">true</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> SVGGraphicsElement</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    inlineStyle</span><span style="color:#F8F8F2">({clone, style});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> base64SVG</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#F8F8F2">      window.</span><span style="color:#50FA7B">btoa</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6;font-weight:bold">new</span><span style="color:#50FA7B"> XMLSerializer</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">serializeToString</span><span style="color:#F8F8F2">(clone));</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> imgSrc</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#F1FA8C"> `data:image/svg+xml;base64,</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">base64SVG</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> image </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Image</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    image.crossOrigin </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">anonymous</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    image.</span><span style="color:#50FA7B">onload</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">      const</span><span style="color:#F8F8F2"> canvas</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLCanvasElement</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#F8F8F2">                    document.</span><span style="color:#50FA7B">createElement</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">canvas</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">      canvas.width </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> width;</span></span>
<span class="line"><span style="color:#F8F8F2">      canvas.height </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> height;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">      const</span><span style="color:#F8F8F2"> context</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> CanvasRenderingContext2D</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#F8F8F2">        canvas.</span><span style="color:#50FA7B">getContext</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">2d</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">      context?.</span><span style="color:#50FA7B">drawImage</span><span style="color:#F8F8F2">(image, </span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">, width, height);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">      resolve</span><span style="color:#F8F8F2">(canvas);</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    image.src </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> imgSrc;</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>As I modified its declaration, I also had to change the caller in order to find the style of the text element.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Method</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">async </span><span style="color:#50FA7B">toBlob</span><span style="color:#F8F8F2">(type: string </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">image/webp</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">): </span><span style="color:#8BE9FD;font-style:italic">Promise</span><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2">Blob</span><span style="color:#FF79C6">></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  const style</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> CSSStyleDeclaration </span><span style="color:#FF79C6">|</span><span style="color:#BD93F9"> undefined</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">  this</span><span style="color:#F8F8F2">.textRef </span><span style="color:#FF79C6">?</span><span style="color:#50FA7B"> getComputedStyle</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.textRef) </span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  const canvas: HTMLCanvasElement </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#50FA7B"> svgToCanvas</span><span style="color:#F8F8F2">({svg</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.svgRef, style});</span></span>
<span class="line"><span style="color:#F8F8F2">  return </span><span style="color:#50FA7B">canvasToBlob</span><span style="color:#F8F8F2">({canvas, type});</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h4>Canvas To Image (Blob)</h4>
<p>Converting the canvas to an image results in a blob. In my original solution, I implemented that transformation with the help of the fetch API. It’s clean and concise.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> canvasToBlob </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#50FA7B">  async</span><span style="color:#F8F8F2"> ({canvas, type}: {canvas</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> HTMLCanvasElement; type: string}):</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">    Promise</span><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2">Blob</span><span style="color:#FF79C6">></span><span style="color:#FF79C6"> =></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> dataUrl</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> canvas.</span><span style="color:#50FA7B">toDataURL</span><span style="color:#F8F8F2">(type);</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">await</span><span style="color:#50FA7B"> fetch</span><span style="color:#F8F8F2">(dataUrl)).</span><span style="color:#50FA7B">blob</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  };</span></span></code></pre><p>However, once again you might say 😅, I discovered an issue at runtime when I deployed my application.</p>
<p>That approach requires enabling <code>data:</code> in the <code>connect-src</code> rule of the content security policy (CSP) which is strongly discouraged.</p>
<p>Fortunately, there is another way to convert a canvas to a blob, the built-in <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob" rel="noopener noreferrer external">toBlob()</a> method that accept a <code>callback</code> as argument.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> canvasToBlob </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#50FA7B">  async</span><span style="color:#F8F8F2"> ({canvas, type}: {canvas</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> HTMLCanvasElement; type: string}):</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">    Promise</span><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2">Blob</span><span style="color:#FF79C6">></span><span style="color:#FF79C6"> =></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Blob</span><span style="color:#F8F8F2">>((</span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> canvas.</span><span style="color:#50FA7B">toBlob</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">blob</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Blob</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> resolve</span><span style="color:#F8F8F2">(blob), type));</span></span>
<span class="line"><span style="color:#F8F8F2">  };</span></span></code></pre><hr />
<h3>Conclusion</h3>
<p>Sometimes development takes a bit more time than excepted, it took me a day to develop, solve all issues, test and publish the component, but I am glad I did it.</p>
<p>Not only it resolves a feature I needed to (among others) publish this blog post, but I learned quite a few new tricks along the way.</p>
<p>Merry Christmas 🎄<br />
David</p>
<hr />
<h3>Further Reading</h3>
<p>Wanna read more about our project? We are porting <a href="https://deckdeckgo.com/" rel="noopener noreferrer external">DeckDeckGo</a> to <a href="https://dfinity.org/" rel="noopener noreferrer external">DFINITY’s</a> Internet Computer. Here is the list of blog posts I published since we started the project:</p>
<ul>
<li><a href="https://daviddalbusco.com/blog/a-simple-keyval-store-implemented-in-motoko" rel="noopener noreferrer external">A Simple KeyVal Store Implemented in Motoko</a></li>
<li><a href="https://daviddalbusco.com/blog/typescript-utilities-for-candid" rel="noopener noreferrer external">TypeScript Utilities For Candid</a></li>
<li><a href="https://daviddalbusco.com/blog/bye-bye-amazon-and-google-hello-web-3-0" rel="noopener noreferrer external">Bye-Bye Amazon &amp; Google, Hello Web 3.0</a></li>
<li><a href="https://daviddalbusco.com/blog/dynamically-import-esm-modules-from-a-cdn" rel="noopener noreferrer external">Dynamically Import ESM Modules From A CDN</a></li>
<li><a href="https://daviddalbusco.com/blog/internet-computer-web-app-decentralized-database-architecture" rel="noopener noreferrer external">Internet Computer: Web App Decentralized Database Architecture</a></li>
<li><a href="https://daviddalbusco.com/blog/singleton-and-factory-patterns-with-typescript" rel="noopener noreferrer external">Singleton &amp; Factory Patterns With TypeScript</a></li>
<li><a href="https://daviddalbusco.com/blog/getting-started-with-the-internet-computer-web-hosting" rel="noopener noreferrer external">Hosting on the Internet Computer</a></li>
<li><a href="https://daviddalbusco.com/blog/we-received-a-grant-to-port-our-web-app-to-the-internet-computer" rel="noopener noreferrer external">We Received A Grant To Port Our Web App To The Internet Computer</a></li>
</ul>
<hr />
<h3>Keep In Touch</h3>
<p>To follow our adventure, you can star and watch our <a href="https://github.com/deckgo/deckdeckgo" rel="noopener noreferrer external">GitHub repo</a> ⭐️ and <a href="https://eepurl.com/hKeMLD" rel="noopener noreferrer external">sign up</a> to join the list of beta tester.</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[A Simple KeyVal Store Implemented in Motoko]]></title>
          <description><![CDATA[Build a generic keyval store for canister smart contract on the Internet Computer.]]></description>
          <link>https://daviddalbusco.com/blog/a-simple-keyval-store-implemented-in-motoko/</link>
          <pubDate>Tue, 30 Nov 2021 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*TtIqarWb4Sbvh2lntKUlHg.jpeg" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@peterlaster?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Pedro Lastra</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<hr />
<p>I am a fond of offline web applications and most of my personal open source projects, such as <a href="https://deckdeckgo.com/" rel="noopener noreferrer external">DeckDeckGo</a> or <a href="https://tietracker.app.link/" rel="noopener noreferrer external">Tie Tracker</a>, follow the approach.</p>
<p>In these two particular apps, I use <a href="https://github.com/jakearchibald/idb-keyval" rel="noopener noreferrer external">idb-keyval</a> to ease the interaction with IndexedDB through a keyval-like API.</p>
<p>That’s why, in the last iteration of our migration to the <a href="https://dfinity.org/" rel="noopener noreferrer external">DFINITY</a>’s Internet Computer, I developed a generic store for canister smart contract in <a href="https://smartcontracts.org/docs/language-guide/motoko.html" rel="noopener noreferrer external">Motoko</a> that also maintain data with key and values.</p>
<hr />
<h3>Store</h3>
<p>My goal is to be able to reuse the same store across canisters and projects, multiple times. If one of my actor would contain different types of data, for example cars and vegetables, I would like to re-use the same helper that encapsulates the data and exposes functions such as: <code>put</code>, <code>get</code>, <code>delete</code> and <code>list</code>.</p>
<p>Therefore, the store I developed is nothing less than a generic class that uses a <a href="https://sdk.dfinity.org/docs/base-libraries/hashmap" rel="noopener noreferrer external">HashMap</a> for the persistence with textual keys (type <a href="https://smartcontracts.org/docs/base-libraries/Text.html" rel="noopener noreferrer external">Text</a>).</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> Text </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">mo:base/Text</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">import HashMap </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">mo:base/HashMap</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">import Iter </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">mo:base/Iter</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">import Array </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">mo:base/Array</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">module {</span></span>
<span class="line"><span style="color:#F8F8F2">    public class DataStore&#x3C;T>() {</span></span>
<span class="line"><span style="color:#F8F8F2">        private var data: HashMap.HashMap&#x3C;Text, T> =</span></span>
<span class="line"><span style="color:#F8F8F2">                HashMap.HashMap&#x3C;Text, T>(10, Text.equal, Text.hash);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>Put, Get &amp; Delete</h3>
<p>Functions that modifies the state are basically applying changes directly to the <code>HashMap</code> except the deletion operation, which I extended with a <code>getter</code>, even though a <code>delete</code> does not do anything if the key does not exist. I thought occasionally it can be interesting to get back the value of the key that has been deleted.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">public func </span><span style="color:#50FA7B">put</span><span style="color:#F8F8F2">(key: Text, value: T) {</span></span>
<span class="line"><span style="color:#F8F8F2">    data.</span><span style="color:#50FA7B">put</span><span style="color:#F8F8F2">(key, value);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">public func </span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(key: Text): </span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2">T {</span></span>
<span class="line"><span style="color:#F8F8F2">    return data.get(key);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">public func </span><span style="color:#50FA7B">del</span><span style="color:#F8F8F2">(key: Text)</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> ?</span><span style="color:#F8F8F2">T {</span></span>
<span class="line"><span style="color:#F8F8F2">    let entry</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> ?</span><span style="color:#F8F8F2">T </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> get</span><span style="color:#F8F8F2">(key);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    switch</span><span style="color:#F8F8F2"> (entry) {</span></span>
<span class="line"><span style="color:#50FA7B">        case</span><span style="color:#F8F8F2"> (?</span><span style="color:#FFB86C;font-style:italic">entry</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">            data.</span><span style="color:#50FA7B">delete</span><span style="color:#F8F8F2">(key);</span></span>
<span class="line"><span style="color:#F8F8F2">        };</span></span>
<span class="line"><span style="color:#50FA7B">        case</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">null</span><span style="color:#F8F8F2">) {};</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    return entry;</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><hr />
<h3>List</h3>
<p>Getting a list of all entries of the store would also not be much more than querying the <code>HashMap</code> directly if it were not for the possibility of filtering the data. Indeed, it might be interesting to search only keys that start with or contain a particular prefix.</p>
<p>I firstly defined a new type <code>DataFilter</code> for the filter and implemented the effective filtering functions that acknowledge the optional nature of these options.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">public </span><span style="color:#FF79C6">type</span><span style="color:#8BE9FD;font-style:italic"> DataFilter</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    startsWith</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> ?</span><span style="color:#8BE9FD;font-style:italic">Text</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    contains</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> ?</span><span style="color:#8BE9FD;font-style:italic">Text</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">private func </span><span style="color:#50FA7B">keyStartsWith</span><span style="color:#F8F8F2">(key: Text, startsWith: </span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2">Text)</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> Bool {</span></span>
<span class="line"><span style="color:#50FA7B">    switch</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">startsWith</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        case</span><span style="color:#BD93F9"> null</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        };</span></span>
<span class="line"><span style="color:#FF79C6">        case</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2">startsWith) {</span></span>
<span class="line"><span style="color:#F8F8F2">            return Text.startsWith(key, #text startsWith);</span></span>
<span class="line"><span style="color:#F8F8F2">        };</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">private func </span><span style="color:#50FA7B">keyContains</span><span style="color:#F8F8F2">(key: Text, contains: </span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2">Text)</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> Bool {</span></span>
<span class="line"><span style="color:#50FA7B">    switch</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">contains</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        case</span><span style="color:#BD93F9"> null</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        };</span></span>
<span class="line"><span style="color:#FF79C6">        case</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2">contains) {</span></span>
<span class="line"><span style="color:#F8F8F2">            return Text.contains(key, #text contains);</span></span>
<span class="line"><span style="color:#F8F8F2">        };</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>The above functions are returning <code>true</code> if no filters are defined, assuming <code>undefined</code> means “ignore the option”. There is probably a better way of implementing such condition in Motoko but, I am not yet as fluid in it as I am with others languages such as TypeScript. If you are up to improve the solution, go for it, send me a <a href="https://github.com/deckgo/deckdeckgo/blob/main/canisters/src/data/data.filter.mo" rel="noopener noreferrer external">Pull Request</a>!</p>
<p>Finally, I implemented the <code>list</code> function itself that either returns all entries or applies the filter following an <code>and</code> logic.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">public func </span><span style="color:#50FA7B">list</span><span style="color:#F8F8F2">(filter: </span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2">DataFilter)</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [(Text, T)] {</span></span>
<span class="line"><span style="color:#F8F8F2">    let entries</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> Iter.Iter</span><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2">(Text, T)</span><span style="color:#FF79C6">></span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> data.</span><span style="color:#50FA7B">entries</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    switch</span><span style="color:#F8F8F2"> (filter) {</span></span>
<span class="line"><span style="color:#F8F8F2">        case null {</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#F8F8F2"> Iter.</span><span style="color:#50FA7B">toArray</span><span style="color:#F8F8F2">(entries);</span></span>
<span class="line"><span style="color:#F8F8F2">        };</span></span>
<span class="line"><span style="color:#50FA7B">        case</span><span style="color:#F8F8F2"> (?</span><span style="color:#FFB86C;font-style:italic">filter</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">            let</span><span style="color:#F8F8F2"> keyValues</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [(</span><span style="color:#FFB86C;font-style:italic">Text</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">)] </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> Iter.</span><span style="color:#50FA7B">toArray</span><span style="color:#F8F8F2">(entries);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">            let</span><span style="color:#F8F8F2"> {startsWith; contains} </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> filter;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">            let</span><span style="color:#F8F8F2"> values</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [(</span><span style="color:#FFB86C;font-style:italic">Text</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">)] </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#F8F8F2">                        Array.mapFilter</span><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2">(Text, T), (Text, T)></span></span>
<span class="line"><span style="color:#F8F8F2">            (keyValues, </span><span style="color:#50FA7B">func</span><span style="color:#F8F8F2"> ((</span><span style="color:#FFB86C;font-style:italic">key</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Text</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> T</span><span style="color:#F8F8F2">)) </span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> ?</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">Text</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#50FA7B">                if</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">keyStartsWith</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">key</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">startsWith</span><span style="color:#F8F8F2">) and</span></span>
<span class="line"><span style="color:#50FA7B">                    keyContains</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">key</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">contains</span><span style="color:#F8F8F2">)) {</span></span>
<span class="line"><span style="color:#FF79C6">                    return</span><span style="color:#FF79C6"> ?</span><span style="color:#F8F8F2">(key, value);</span></span>
<span class="line"><span style="color:#F8F8F2">                };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">                return </span><span style="color:#BD93F9">null</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">            });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">            return values;</span></span>
<span class="line"><span style="color:#F8F8F2">        };</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><hr />
<h3>Upgrades</h3>
<p>To preserve the state of the canisters on upgrades, <a href="https://smartcontracts.org/docs/language-guide/upgrades.html#_preupgrade_and_postupgrade_system_methods" rel="noopener noreferrer external">preupgrade and postupgrade</a> system hooks can be implemented for variable that are not stable per default. To foresee such process, I also added two final functions to the store.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">public func </span><span style="color:#50FA7B">preupgrade</span><span style="color:#F8F8F2">(): HashMap.HashMap</span><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2">Text, T</span><span style="color:#FF79C6">></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    return data;</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">public func </span><span style="color:#50FA7B">postupgrade</span><span style="color:#F8F8F2">(stableData: [(Text, T)]) {</span></span>
<span class="line"><span style="color:#F8F8F2">    data :</span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> HashMap.</span><span style="color:#50FA7B">fromIter</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Text</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">>(stableData.</span><span style="color:#50FA7B">vals</span><span style="color:#F8F8F2">(), </span><span style="color:#BD93F9">10</span><span style="color:#F8F8F2">, Text.equal, Text.hash);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><hr />
<h3>Usage</h3>
<p>To showcase the usage of such generic store in an actor, we create an empty canister that defines a type of object to store, such as a <code>Car</code> .</p>
<p>We <code>import</code> the helper and declare both the objects we are going to use. The <code>store</code> itself and a stable <code>entries</code> to maintain the state on upgrades.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> Iter </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">mo:base/Iter</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">import DataStore </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">./store</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">actor Test {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    type</span><span style="color:#F8F8F2"> Car = {</span></span>
<span class="line"><span style="color:#F8F8F2">        name: Text;</span></span>
<span class="line"><span style="color:#F8F8F2">        manufacturer: Text;</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    private let store: DataStore.DataStore&#x3C;Car> =</span></span>
<span class="line"><span style="color:#F8F8F2">                       DataStore.DataStore&#x3C;Car>();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    private stable var entries : [(Text, Car)] = [];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    system func preupgrade() {</span></span>
<span class="line"><span style="color:#F8F8F2">        entries := Iter.toArray(store.preupgrade().entries());</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    system func postupgrade() {</span></span>
<span class="line"><span style="color:#F8F8F2">        store.postupgrade(entries);</span></span>
<span class="line"><span style="color:#F8F8F2">        entries := [];</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Once these defined, we expose the functions that modify the state and link these with the store.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">public query func </span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(key: Text) : </span><span style="color:#50FA7B">async</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2">Car) {</span></span>
<span class="line"><span style="color:#F8F8F2">    let entry</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> ?</span><span style="color:#F8F8F2">Car </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> store.</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(key);</span></span>
<span class="line"><span style="color:#F8F8F2">    return entry;</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">public func </span><span style="color:#50FA7B">set</span><span style="color:#F8F8F2">(key: Text, data: Car) </span><span style="color:#FF79C6">:</span><span style="color:#50FA7B"> async</span><span style="color:#F8F8F2"> () {</span></span>
<span class="line"><span style="color:#F8F8F2">    store.put(key, data);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">public func </span><span style="color:#50FA7B">del</span><span style="color:#F8F8F2">(key: Text) : </span><span style="color:#50FA7B">async</span><span style="color:#F8F8F2"> () {</span></span>
<span class="line"><span style="color:#F8F8F2">    let entry</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> ?</span><span style="color:#F8F8F2">Car </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> store.</span><span style="color:#50FA7B">del</span><span style="color:#F8F8F2">(key);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Finally, we plug the last bit of code, the function that lists and filters the entries.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">public query func </span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(key: Text) : </span><span style="color:#50FA7B">async</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2">Car) {</span></span>
<span class="line"><span style="color:#F8F8F2">    let entry</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> ?</span><span style="color:#F8F8F2">Car </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> store.</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(key);</span></span>
<span class="line"><span style="color:#F8F8F2">    return entry;</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Et voilà, with few lines of code, we have implemented a simple keyval canister smart contract that store our data 🥳.</p>
<hr />
<h3>Playground</h3>
<p>Wanna play with the previous example and store? Checkout this <a href="https://m7sm4-2iaaa-aaaab-qabra-cai.raw.ic0.app/?tag=1517752901" rel="noopener noreferrer external">Motoko Playground</a> and have fun 🤙.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*CgTzjuNqFv3mr1IpCZsPgw.png" alt="" / loading="lazy"></p>
<hr />
<h3>Further Reading</h3>
<p>Wanna read more our project? Here is the list of blog posts I published since we started the project with the Internet Computer:</p>
<ul>
<li><a href="https://daviddalbusco.com/blog/typescript-utilities-for-candid" rel="noopener noreferrer external">TypeScript Utilities For Candid</a></li>
<li><a href="https://daviddalbusco.com/blog/bye-bye-amazon-and-google-hello-web-3-0" rel="noopener noreferrer external">Bye-Bye Amazon &amp; Google, Hello Web 3.0</a></li>
<li><a href="https://daviddalbusco.com/blog/dynamically-import-esm-modules-from-a-cdn" rel="noopener noreferrer external">Dynamically Import ESM Modules From A CDN</a></li>
<li><a href="https://daviddalbusco.com/blog/internet-computer-web-app-decentralized-database-architecture" rel="noopener noreferrer external">Internet Computer: Web App Decentralized Database Architecture</a></li>
<li><a href="https://daviddalbusco.com/blog/singleton-and-factory-patterns-with-typescript" rel="noopener noreferrer external">Singleton &amp; Factory Patterns With TypeScript</a></li>
<li><a href="https://daviddalbusco.com/blog/getting-started-with-the-internet-computer-web-hosting" rel="noopener noreferrer external">Hosting on the Internet Computer</a></li>
<li><a href="https://daviddalbusco.com/blog/we-received-a-grant-to-port-our-web-app-to-the-internet-computer" rel="noopener noreferrer external">We Received A Grant To Port Our Web App To The Internet Computer</a></li>
</ul>
<hr />
<h3>Keep In Touch</h3>
<p>To follow our adventure, you can star and watch our <a href="https://github.com/deckgo/deckdeckgo" rel="noopener noreferrer external">GitHub repo</a> ⭐️ and <a href="https://eepurl.com/hKeMLD" rel="noopener noreferrer external">sign up</a> to join the list of beta tester.</p>
<hr />
<h3>Conclusion</h3>
<p>There might be a better way to implement the filtering options and, not sure such the architecture is the state of the art (do other Motoko developers create stores next to their canisters?).</p>
<p>However, it fits very well my projects and, as I am still learning, it can only be improved within time because I am porting our web editor to <a href="https://dfinity.org/" rel="noopener noreferrer external">DFINITY</a>’s Internet Computer and do not intend to stop soon.</p>
<p>To infinity and beyond!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[TypeScript Utilities For Candid]]></title>
          <description><![CDATA[A collection of functions to handle Nullable, Date and Blob when interacting with canister smart contracts.]]></description>
          <link>https://daviddalbusco.com/blog/typescript-utilities-for-candid/</link>
          <pubDate>Tue, 16 Nov 2021 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*-zlHKiCIag6cmR5WO2Z_RQ.jpeg" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@georgie_cobbs?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Georgie Cobbs</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<hr />
<p>To port our web editor, <a href="https://deckdeckgo.com/" rel="noopener noreferrer external">DeckDeckGo</a>, to <a href="https://dfinity.org/" rel="noopener noreferrer external">DFINITY</a>’s Internet Computer I developed several helpers in TypeScript to interact with our canister smart contracts.</p>
<p>If it can make your life easier too, here are those I use the most.</p>
<hr />
<h3>Nullable</h3>
<p>The <a href="https://smartcontracts.org/docs/candid-guide/candid-concepts.html" rel="noopener noreferrer external">Candid</a> description that is generated for nullable types does not exactly match what I commonly used in JavaScript for optional types (see this <a href="https://forum.dfinity.org/t/candid-code-generation-for-nullable-types/6820" rel="noopener noreferrer external">post</a> for the why and how).</p>
<p>For example, if we generate an interface for such a <a href="https://smartcontracts.org/docs/language-guide/motoko.html" rel="noopener noreferrer external">Motoko</a> code snippet:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">actor Example {</span></span>
<span class="line"><span style="color:#F8F8F2">  public shared query func </span><span style="color:#50FA7B">list</span><span style="color:#F8F8F2">(filter: </span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2">Text) </span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> async [Text] {</span></span>
<span class="line"><span style="color:#F8F8F2">    let results</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [Text] </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> myFunction</span><span style="color:#F8F8F2">(filter);</span></span>
<span class="line"><span style="color:#F8F8F2">    return results;</span></span>
<span class="line"><span style="color:#F8F8F2">  };</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>The definition of the optional parameter <code>filter</code> will not be interpreted as a <code>string</code> that can potentially be <code>undefined</code> but, rather as a one-element length <code>array</code> that contains a <code>string</code> or is empty.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> interface</span><span style="color:#8BE9FD;font-style:italic"> _SERVICE</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">  list</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">arg_0</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [] </span><span style="color:#FF79C6">|</span><span style="color:#F8F8F2"> [</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">]) </span><span style="color:#FF79C6">=></span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Array</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">>>;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>That is why I created functions to convert back and forth optional values.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> toNullable</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#8BE9FD;font-style:italic">T</span><span style="color:#F8F8F2">>(value?: T): [] | [T] => </span><span style="color:#FF79C6">{</span></span>
<span class="line"><span style="color:#F8F8F2">  return value </span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2"> [value] </span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [];</span></span>
<span class="line"><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">export const fromNullable = &#x3C;</span><span style="color:#8BE9FD;font-style:italic">T</span><span style="color:#F8F8F2">>(value: [] | [T]): T | undefined => </span><span style="color:#FF79C6">{</span></span>
<span class="line"><span style="color:#F8F8F2">  return value?.[</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">];</span></span>
<span class="line"><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">;</span></span></code></pre><p><code>toNullable</code> convert an object that can either be of type <code>T</code> or <code>undefined</code> to what’s expected to interact with the IC and, <code>fromNullable</code> do the opposite.</p>
<hr />
<h3>Dates</h3>
<p>System <a href="https://smartcontracts.org/docs/base-libraries/Time.html" rel="noopener noreferrer external">Time</a> (nanoseconds since 1970–01–01) gets parsed to <code>bigint</code> and exported as a type <code>Time</code> in Candid definition.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> type</span><span style="color:#8BE9FD;font-style:italic"> Time</span><span style="color:#FF79C6"> =</span><span style="color:#8BE9FD;font-style:italic"> bigint</span><span style="color:#F8F8F2">;</span></span></code></pre><p>To convert JavaScript <code>Date</code> to big numbers, the built-in object <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt" rel="noopener noreferrer external">BigInt</a> can be instantiated by multiplying seconds to nano seconds.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> toTimestamp</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Date</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Time</span><span style="color:#FF79C6"> =></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#50FA7B"> BigInt</span><span style="color:#F8F8F2">(value.</span><span style="color:#50FA7B">getTime</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">*</span><span style="color:#BD93F9"> 1000</span><span style="color:#FF79C6"> *</span><span style="color:#BD93F9"> 1000</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>The other way around works by converting first the big numbers to their primitive <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number" rel="noopener noreferrer external">Number</a> types and dividing it to seconds.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> fromTimestamp</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Time</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Date</span><span style="color:#FF79C6"> =></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Date</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">Number</span><span style="color:#F8F8F2">(value) </span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2"> (</span><span style="color:#BD93F9">1000</span><span style="color:#FF79C6"> *</span><span style="color:#BD93F9"> 1000</span><span style="color:#F8F8F2">));</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>To support <code>Nullable</code> timestamps values, I also created the following helpers that extend above converters and return the appropriate optional arrays.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> toNullableTimestamp</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">value</span><span style="color:#FF79C6">?:</span><span style="color:#8BE9FD;font-style:italic"> Date</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [] </span><span style="color:#FF79C6">|</span><span style="color:#F8F8F2"> [</span><span style="color:#8BE9FD;font-style:italic">Time</span><span style="color:#F8F8F2">] </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> time</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> value?.</span><span style="color:#50FA7B">getTime</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#F8F8F2"> value </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#FF79C6"> !</span><span style="color:#50FA7B">isNaN</span><span style="color:#F8F8F2">(time) </span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2"> [</span><span style="color:#50FA7B">toTimestamp</span><span style="color:#F8F8F2">(value)] </span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [];</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> fromNullableTimestamp </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#F8F8F2">       (</span><span style="color:#FFB86C;font-style:italic">value</span><span style="color:#FF79C6">?:</span><span style="color:#F8F8F2"> [] </span><span style="color:#FF79C6">|</span><span style="color:#F8F8F2"> [</span><span style="color:#8BE9FD;font-style:italic">Time</span><span style="color:#F8F8F2">])</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Date</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> =></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#FF79C6"> !</span><span style="color:#50FA7B">isNaN</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">parseInt</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">value?.[</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">]</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">)) </span><span style="color:#FF79C6">?</span></span>
<span class="line"><span style="color:#50FA7B">    fromTimestamp</span><span style="color:#F8F8F2">(value[</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">]) </span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><hr />
<h3>Blob</h3>
<p>Binary <a href="https://smartcontracts.org/docs/base-libraries/Blob.html" rel="noopener noreferrer external">blobs</a> are described in Candid as <code>Array</code> of <code>numbers</code>. To save untyped data in smart contracts (assuming the use case allows such risk) while still preserving types on the frontend side, we can <code>stringify</code> objects, converts these to blobs and gets their contents as binary data contained in an <code>ArrayBuffer</code>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> toArray </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#FF79C6">       async</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">>(</span><span style="color:#FFB86C;font-style:italic">data</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> T</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Array</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">number</span><span style="color:#F8F8F2">>> </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> blob</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Blob</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Blob</span><span style="color:#F8F8F2">([</span><span style="color:#BD93F9">JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">stringify</span><span style="color:#F8F8F2">(data)],</span></span>
<span class="line"><span style="color:#F8F8F2">                         {type</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">application/json; charset=utf-8</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">});</span></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#F8F8F2"> [</span><span style="color:#FF79C6">...</span><span style="color:#FF79C6;font-weight:bold">new</span><span style="color:#50FA7B"> Uint8Array</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">await</span><span style="color:#F8F8F2"> blob.</span><span style="color:#50FA7B">arrayBuffer</span><span style="color:#F8F8F2">())];</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>To convert back an <code>Array</code> of <code>numbers</code> to a specific object type, the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Blob" rel="noopener noreferrer external">Blob</a> type can be used again but, this time a textual conversion shall be used to parse the results.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> fromArray </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#FF79C6">       async</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">>(</span><span style="color:#FFB86C;font-style:italic">data</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Array</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">number</span><span style="color:#F8F8F2">>)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> blob</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Blob</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Blob</span><span style="color:#F8F8F2">([</span><span style="color:#FF79C6;font-weight:bold">new</span><span style="color:#50FA7B"> Uint8Array</span><span style="color:#F8F8F2">(data)],</span></span>
<span class="line"><span style="color:#F8F8F2">                         {type</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">application/json; charset=utf-8</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">});</span></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#BD93F9"> JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">parse</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">await</span><span style="color:#F8F8F2"> blob.</span><span style="color:#50FA7B">text</span><span style="color:#F8F8F2">());</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Both conversions are asynchronous because interacting with the blob object requires resolving promises in JavaScript.</p>
<hr />
<h3>Further Reading</h3>
<p>Wanna read more our project? Here is the list of blog posts I published since we started the project with the Internet Computer:</p>
<ul>
<li><a href="https://daviddalbusco.com/blog/bye-bye-amazon-and-google-hello-web-3-0" rel="noopener noreferrer external">Bye-Bye Amazon &amp; Google, Hello Web 3.0</a></li>
<li><a href="https://daviddalbusco.com/blog/dynamically-import-esm-modules-from-a-cdn" rel="noopener noreferrer external">Dynamically Import ESM Modules From A CDN</a></li>
<li><a href="https://daviddalbusco.com/blog/internet-computer-web-app-decentralized-database-architecture" rel="noopener noreferrer external">Internet Computer: Web App Decentralized Database Architecture</a></li>
<li><a href="https://daviddalbusco.com/blog/singleton-and-factory-patterns-with-typescript" rel="noopener noreferrer external">Singleton &amp; Factory Patterns With TypeScript</a></li>
<li><a href="https://daviddalbusco.com/blog/getting-started-with-the-internet-computer-web-hosting" rel="noopener noreferrer external">Hosting on the Internet Computer</a></li>
<li><a href="https://daviddalbusco.com/blog/we-received-a-grant-to-port-our-web-app-to-the-internet-computer" rel="noopener noreferrer external">We Received A Grant To Port Our Web App To The Internet Computer</a></li>
</ul>
<hr />
<h3>Keep In Touch</h3>
<p>To follow our adventure, you can star and watch our <a href="https://github.com/deckgo/deckdeckgo" rel="noopener noreferrer external">GitHub repo</a> ⭐️ and <a href="https://eepurl.com/hKeMLD" rel="noopener noreferrer external">sign up</a> to join the list of beta tester.</p>
<hr />
<h3>Conclusion</h3>
<p>I hope this short blog post and few utilities will be useful for you to start well with the Internet Computer, it is really a fun technology.</p>
<p>To infinity and beyond!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Bye-Bye Amazon & Google, Hello Web 3.0]]></title>
          <description><![CDATA[We are migrating our web application to DFINITY's Internet Computer.]]></description>
          <link>https://daviddalbusco.com/blog/bye-bye-amazon-and-google-hello-web-3-0/</link>
          <pubDate>Mon, 11 Oct 2021 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*YNwx6BRv02H0wpV7xsKhtg.png" alt="" / loading="lazy"></p>
<p>It’s official, we are porting our web editor for slides, <a href="https://deckdeckgo.com/" rel="noopener noreferrer external">DeckDeckGo</a>, to <a href="https://dfinity.org/" rel="noopener noreferrer external">DFINITY</a>’s Internet Computer!</p>
<p>Thanks to the help of a second 25k USD grant from the DFINITY foundation, we will wrap up the work needed for running a beta version of DeckDeckGo on the Internet Computer (<a href="https://eepurl.com/hKeMLD" rel="noopener noreferrer external">sign up</a> to help us test it!).</p>
<hr />
<h3>Why?</h3>
<p>Of course getting grants is a strong motivation to use a new technology, but there are other reasons why we think this is the right move.</p>
<hr />
<h4>Decentralization</h4>
<p>Even though at first glance <strong>decentralization</strong> might not seem to bring much to a presentation editor (who needs decentralized slides?), we think the extra level of <strong>security</strong> it adds is an incredible opportunity.</p>
<p>We foresee an architecture in which each user gets one (or multiple) database-like smart contracts for their data. Users will get a clear ownership on their data and there won’t be one single node containing all the content.</p>
<p>Moreover, thanks to the <a href="https://sdk.dfinity.org/docs/ic-identity-guide/what-is-ic-identity.html" rel="noopener noreferrer external">Internet Identity</a> that enables a secure anonymous authentication, the <strong>privacy</strong> will also be improved.</p>
<p>Finally, the nature of the system makes it also unstoppable and less prone to failure.</p>
<p>Think about it, as I am writing these lines Wednesday Oct. 6, it was just reported that Twitch reportedly suffered a massive data leak. It was reported that more than 100GB of data (including revenue, activities and even passwords) is available online.</p>
<p>In addition, two days ago, Monday Oct. 4, Facebook suffered a massive blackout of seven hours during which none of their services were reachable. It was even reported that some employees could not access their building because the doors were connected to the same networks that were unreachable.</p>
<p>Decentralization is no silver bullet, but these two recent incidents of major platforms showed me once again that the current model has its limits and comforts me in our decision.</p>
<hr />
<h4>Governance</h4>
<p>As a decentralized blockchain, all changes to the configuration and behavior of the Internet Computer are controlled by a (decentralized, democratic) governance body called the Network Nervous System (NNS — <a href="https://sdk.dfinity.org/docs/developers-guide/concepts/governance.html" rel="noopener noreferrer external">source</a>).</p>
<p>According to certain criteria (see “neurons” in the linked source), it is possible to get voting rights to define the future of the network.</p>
<p>In other words, it is possible to become an actor and to get involved in the development of the network.</p>
<hr />
<h4>Open Source</h4>
<p>We share our learnings through blog posts and our project’s <strong>open source</strong> code is published on <a href="https://github.com/deckgo/deckdeckgo/" rel="noopener noreferrer external">GitHub</a>.</p>
<p>Using a technology and providers that share and live by the same values goes without saying.</p>
<hr />
<h4>GAFAM</h4>
<p>I originally intended to hit quite hard on the big tech in this chapter because I am convinced there is something viscerally rotten in their monopolistic influences on our life. Instead I will leave you with one simple question: do you really want to pay for services that send Jeff Bezos to space?</p>
<hr />
<h3>When?</h3>
<p>We have no exact goal on the production launch date. On the contrary, without pressuring ourselves too much, we do have for objective to reach a Beta milestone by the end of the year or the beginning of the next. At that time, we hope to count on you to help us test the platform before its official release.</p>
<p>Meanwhile, we are also planning to unleash in production a transitional version of our editor that has been transformed to an “offline first” approach.</p>
<p>Because we are planning no migration of the data, it will allow users to save their content to static files. The new version of the editor can open and save presentations to files as you would do with any desktop applications.</p>
<hr />
<h3>How?</h3>
<p>To complete the proof of concept of porting DeckDeckGo on DFINITY's Internet Computer, we already completed three milestones: “Offline first and Hosting”, “Authentication” and “Data Persistence”.</p>
<p>In order to achieve our short-term goal, and to get our second grant 😜, we have defined the following two new milestones:</p>
<hr />
<h4>Milestone 4 — Assets</h4>
<p>The Internet Computer is primarily a distributed and decentralized platform for running software. To persist data and run programs on it, the applications have to be deployed as WebAssembly modules. These are executed inside of a conceptual computational unit called a software canister (<a href="https://sdk.dfinity.org/docs/developers-guide/concepts/canisters-code.html" rel="noopener noreferrer external">source</a>).</p>
<p>To create slides, users must be able to upload their assets (images and csv files). That data is currently persisted in CDN Storage.</p>
<p>This milestone is the re-implementation of a similar system on the Internet Computer using one or multiple canisters. As for the data, we aim to empower the ownership and privacy of the users’ content with a decentralized architecture.</p>
<hr />
<h4>Milestone 5 — Functions</h4>
<p>Various features that interact with the Internet Computer through async canisters calls are time consuming (e.g. delete all canisters upon user deletion). Tasks like this should be run in the background by dedicated canisters.</p>
<p>In other words: our overall goal is to transform our editor in a way that user should not even notice that it is powered by a blockchain network, its usage should feel like using any other slick modern web application.</p>
<hr />
<h3>Future?</h3>
<p>From day one and until today, DeckDeckGo has always been a side project. Even though we do display an <a href="https://deckdeckgo.com/en/enterprise" rel="noopener noreferrer external">Enterprise</a> offering on our website, we never actively did any sales activities nor found the perfect partners (or time) to start such a collaboration.</p>
<p>However, as we are about to transition to a new network model that requires <a href="https://sdk.dfinity.org/docs/developers-guide/concepts/tokens-cycles.html" rel="noopener noreferrer external">cycles</a>, which are used to pay for resource consumption, and have for goal to release kind of a unique tool, we do know have monetization in mind with nothing less than tokenization.</p>
<hr />
<h3>Further Reading?</h3>
<p>“Sharing is caring”, here is the list of blog posts I published since we started the project with the Internet Computer:</p>
<ul>
<li><a href="https://daviddalbusco.com/blog/we-received-a-grant-to-port-our-web-app-to-the-internet-computer" rel="noopener noreferrer external">We Received A Grant To Port Our Web App To The Internet Computer</a></li>
<li><a href="https://daviddalbusco.com/blog/getting-started-with-the-internet-computer-web-hosting" rel="noopener noreferrer external">Hosting on the Internet Computer</a></li>
<li><a href="https://daviddalbusco.com/blog/singleton-and-factory-patterns-with-typescript" rel="noopener noreferrer external">Singleton &amp; Factory Patterns With TypeScript</a></li>
<li><a href="https://daviddalbusco.com/blog/internet-computer-web-app-decentralized-database-architecture" rel="noopener noreferrer external">Internet Computer: Web App Decentralized Database Architecture</a></li>
<li><a href="https://daviddalbusco.com/blog/dynamically-import-esm-modules-from-a-cdn" rel="noopener noreferrer external">Dynamically Import ESM Modules From A CDN</a></li>
</ul>
<hr />
<h3>Keep In Touch</h3>
<p>To follow our adventure, you can star and watch our <a href="https://github.com/deckgo/deckdeckgo" rel="noopener noreferrer external">GitHub repo</a> ⭐️ and <a href="https://eepurl.com/hKeMLD" rel="noopener noreferrer external">sign up</a> to join the list of beta tester.</p>
<hr />
<h3>Conclusion</h3>
<p>A journey of a thousand miles begins with a single step and this blog post was mine.</p>
<p>To DFINITY and beyond.</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Dynamically Import ESM Modules From A CDN]]></title>
          <description><![CDATA[Lazy load JavaScript code from a content delivery network to serve users only what they need when they need it.]]></description>
          <link>https://daviddalbusco.com/blog/dynamically-import-esm-modules-from-a-cdn/</link>
          <pubDate>Mon, 27 Sep 2021 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*hC1zkJeJsjBGN56FMa5mJA.jpeg" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@danborn?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Daniel Born</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<hr />
<p>What if I told you modern browsers can natively import a single, or sets of functions, of a JavaScript library bundles, at runtime and, from a CDN? Wouldn’t that open up possibilities?</p>
<p>Well, good news. This is not an hypothesis but, a fact. Nowadays, all modern browsers can dynamically import <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules" rel="noopener noreferrer external">JavaScript modules</a> from content delivery networks 🥳.</p>
<hr />
<h3>Introduction</h3>
<p>There are a number of libraries and frameworks that enable module usage when developing web frontend applications. Most apps and libraries will have their files “bundled” using tools like <a href="https://webpack.js.org/" rel="noopener noreferrer external">Webpack</a>, <a href="https://rollupjs.org/" rel="noopener noreferrer external">Rollup</a> or with more recent bundler such as <a href="https://esbuild.github.io/" rel="noopener noreferrer external">esbuild</a>.</p>
<p>Thanks to these tools, the JavaScript code can be analyzed, build and split into smaller chunks.</p>
<p>While this approach works like a charm, it has for downside that ES modules are imported regardless if executed or not.</p>
<p>For example, if you would use an awesome library such as <a href="https://github.com/jakearchibald/idb-keyval" rel="noopener noreferrer external">idb-keyval</a> to print out a value stored in IndexedDB but, had for goal to reserve the function to administrators only.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { get } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">idb-keyval</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> print</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">admin</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">admin) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">await</span><span style="color:#50FA7B"> get</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">hello</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">));</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>If the code is build statically, the third party dependency would be added to the ES modules no matter what. As a result, all users, administrators or not, would have to download the related code even if they would never require it.</p>
<p>This is when dynamic import, part of the <a href="https://github.com/tc39/proposal-dynamic-importhttps://github.com/tc39/proposal-dynamic-import" rel="noopener noreferrer external">official TC39</a> proposal and which has been standardized with <a href="https://tc39.es/ecma262/2020/" rel="noopener noreferrer external">ECMAScript 2020</a>, comes into play.</p>
<p>It tells the browser to load code on demand and only when it is required.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> print</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">admin</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">admin) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { get } </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#FF79C6"> import</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">idb-keyval</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">await</span><span style="color:#50FA7B"> get</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">hello</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">));</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Only the administrators would now have to download the code of the third party library.</p>
<p><em>Note: Dynamic import is not mandatory, your code is all fine. Bundlers and browsers do an incredible job with the dozens of modules of your libraries and applications. It can be helpful in some situations. As in the above example, or when large modules take time to load. It can help improve boot time performance.</em></p>
<hr />
<h3>Dynamic Import + CDN … But Why?</h3>
<p>Imagine now you are developing an application that interact with a backend. Whether you implement your own API or use a third party cloud provider, the code that interact with your data might commonly find place within your frontend app.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*YQ5N22V_d5PVclZ7Ups0jg.png" alt="" / loading="lazy"></p>
<p>As we have seen in previous chapter, if you do not take advantages of “dynamic import”, all users might therefore download the code to interact with the backend (displayed in the schema as “services”) regardless if they use it or not.</p>
<p>While this is probably often not an issue - after all, if the application uses an API, there is a good chance that all users use it - it can become one over time because such design is tight to a vendor lock-in.</p>
<p>Some day, you might have to migrate the application to a new backend or another cloud provider and, the complexity of the upgrade might become a problem.</p>
<p>To prevent such issue, you might extract the “Services” to libraries, giving you more flexibility foreseen the pitfall.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*zppU1zdm2fm1va-X3WRWxw.png" alt="" / loading="lazy"></p>
<p>However, without “dynamic import”, all users might still download all the code, what would be particularly unfortunate if you would ship the application with both “old” and “new” services at some point.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*09yQFPIiesplbZikZN2tEA.png" alt="" / loading="lazy"></p>
<p>Let’s imagine again that everything is in now place. The application leverages “dynamic import”, both “old” and “new” services have been extracted to separate libs.</p>
<p>Even though everything works for the best, at this point, the frontend application has direct dependencies (think <code>npm i lib --save</code>) on these libraries.</p>
<p>If you make any changes in the services, you have to re-build and re-deploy everything, what can quickly become tasky. Especially if you have got not “just” two services but lots of them.</p>
<p>This is where the combination of “dynamic import” and CDN is finally going to shine.</p>
<p>These two strategies applied together can transform the frontend application from a monolith to a modular solution.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*Q4niQVYzosDUo96OS6jr7Q.png" alt="" / loading="lazy"></p>
<p>For each user request, upon a certain condition such as an environment configuration, the frontend application can request at runtime only the function(s), piece of code, needed to perform the specific queries.</p>
<p>Doing so, not just the user is downloading only what is required to perform the queries but, the application also becomes almost independent of the services. Patching these or even adding a new backend can now be developed and deployed without any changes to the application!</p>
<hr />
<h3>Show Me Some Code</h3>
<p>That was a long introduction and explanation, I hope you are still here 😅. A few code is often worth a thousand words, therefore here is how you can dynamically import function(s) from a CDN:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Component, ComponentInterface, h } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@stencil/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  tag</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-home</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  styleUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-home.css</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  shadow</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> AppHome</span><span style="color:#FF79C6"> implements</span><span style="color:#8BE9FD;font-style:italic"> ComponentInterface</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  async</span><span style="color:#50FA7B"> componentDidLoad</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> cdn </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">https://cdn.jsdelivr.net/npm/idb-keyval@6/+es</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> {get, set} </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> import</span><span style="color:#F8F8F2">(cdn);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#50FA7B"> set</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">hello</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">world</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">await</span><span style="color:#50FA7B"> get</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">hello</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">));</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#FF79C6">mark</span><span style="color:#F8F8F2">>dynamic import esm + cdn = ❤️&#x3C;/</span><span style="color:#FF79C6">mark</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>The above code is presented in a <a href="https://stenciljs.com/" rel="noopener noreferrer external">Stencil</a> web component to highlight the fact that it works in any modern browsers.</p>
<p>When the component is mounted, it creates a <code>string</code> variable that point to the ES modules that needs to be loaded.</p>
<p>The usage of such a temporary <code>string</code> value is useful with TypeScript. Without it, the compiler throws an error <code>TS2307: Cannot find module 'https://...' or its corresponding type declarations.</code></p>
<p>The URL of the library on the CDN can then be used to dynamically import, <code>await import(cdn)</code>, the functions we are interested in and voilà, that’s already it 🥳.</p>
<hr />
<h3>Type Safety</h3>
<p>Dynamic import is a native feature, therefore supported out of the box in JavaScript. To improve the syntax with TypeScript, I suggest to use interfaces that can be shared by the consumer (the frontend application) and the libraries (the “Services”).</p>
<p><img src="https://daviddalbusco.com/assets/images/1*ZP2hO1-pD2NH0vLPqDvfJg.png" alt="" / loading="lazy"></p>
<hr />
<p>For example, we can declare an <code>interface</code> of a function that says “hello”:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> interface</span><span style="color:#8BE9FD;font-style:italic"> SayHello</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  ({</span><span style="color:#FFB86C;font-style:italic">name</span><span style="color:#F8F8F2">}</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {name</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">})</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>The <code>library</code> can implements it as follows:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> say</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> SayHello</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#FF79C6">               async</span><span style="color:#F8F8F2"> ({</span><span style="color:#FFB86C;font-style:italic">name</span><span style="color:#F8F8F2">}</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {name</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">})</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#F1FA8C"> `Hello </span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">name</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>The <code>app</code> can also uses the types to improve the syntax:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> function</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> cdn </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">https://..../index.esm.js</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> {say}</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {say</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> SayHello</span><span style="color:#F8F8F2">} </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> import</span><span style="color:#F8F8F2">(cdn);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  await</span><span style="color:#50FA7B"> say</span><span style="color:#F8F8F2">({name</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">David</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">});</span></span>
<span class="line"><span style="color:#F8F8F2">}());</span></span></code></pre><p>Moreover, extracting interfaces and types can also ease new development in the future.</p>
<hr />
<h3>Conclusion</h3>
<p>This approach, “Dynamic Import + CDN”, is not just words to me. It is the core architecture of the next major release(s) of <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>.</p>
<p>Our main application (source <a href="https://github.com/deckgo/deckdeckgo/tree/main/studio" rel="noopener noreferrer external">GitHub</a>), the core, is an offline first editor for slides that has no dependencies to any API.</p>
<p>It “only” consumes a library (source <a href="https://github.com/deckgo/deckdeckgo/tree/main/utils/editor" rel="noopener noreferrer external">GitHub</a>) that describes and exposes the interfaces for endpoints that can be use if a backend is configured.</p>
<p>As we are currently using in production both <a href="https://firebase.google.com/products/firestore" rel="noopener noreferrer external">Google Firestore</a> (see <a href="https://github.com/deckgo/deckdeckgo/tree/main/providers/firebase" rel="noopener noreferrer external">GitHub</a>) and <a href="https://aws.amazon.com/lambda/" rel="noopener noreferrer external">AWS Lambda</a> (see <a href="https://github.com/deckgo/deckdeckgo/tree/main/providers/api" rel="noopener noreferrer external">GitHub</a>), there are two services that are dynamically imported at runtime through a CDN to perform the operations with these cloud providers.</p>
<p>Meanwhile, we have also developed a (successful) proof of concept to port our web app to the <a href="https://dfinity.org/" rel="noopener noreferrer external">DFINITY</a>’s Internet Computer and we are continuing the work towards this direction because, spoiler alert, it is the future. That is why another service interacts with the blockchain network (see <a href="https://github.com/deckgo/deckdeckgo/tree/main/providers/ic" rel="noopener noreferrer external">GitHub</a>).</p>
<p>Finally, thanks to the modular architecture, we are able to quickly integrate our editor in any networks if some clients are looking to use it on-premises.</p>
<p>Summarized: we are future-proof 🚀.</p>
<p>To infinity and beyond!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Angular: The Single Rule To Get Better At RxJS]]></title>
          <description><![CDATA[A guideline that helps develop Angular applications and libraries in a reactive programming way with RxJS.]]></description>
          <link>https://daviddalbusco.com/blog/angular-the-single-rule-to-get-better-at-rxjs/</link>
          <pubDate>Fri, 10 Sep 2021 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/0*nigHuCYvLjp9d92D.jpg" alt="" / loading="lazy"></p>
<hr />
<p>Even though there is no strict rule, combining imperative and declarative programming concepts with <a href="https://rxjs.dev/" rel="noopener noreferrer external">RxJS</a> might be making the development harder and the code less clean.</p>
<p>Shifting mind from a (often) default way of programming (i.e. imperative) to a reactive (i.e. declarative) approach, takes time.</p>
<p>However, there is a single rule that can help you make the switch:</p>
<p>⚠️ Do <strong>not</strong> subscribe, period ⚠️</p>
<hr />
<blockquote>
<p>Confused about the difference between imperative and declarative? Have a look to the excellent <a href="https://eliteionic.com/tutorials/imperative-vs-declarative-programming-with-rxjs-search-filter/" rel="noopener noreferrer external">article</a> of <a href="https://twitter.com/joshuamorony" rel="noopener noreferrer external">Josh Morony</a> that compares both.</p>
</blockquote>
<hr />
<h3>Why?</h3>
<p>The rule is not strict, it is a guideline, like the code of the pirates of the Caribbean 🏴‍☠️.</p>
<p><img src="https://daviddalbusco.com/assets/images/0*VBCiXqgoBrlzo4tQ" alt="" / loading="lazy"></p>
<p>It does not mean that you should never ever subscribe to a stream but rather, that you should try to avoid it. In that way, according my experience, you are going to slowly transform the imperative way of programming into more declarative concepts.</p>
<p>Concretely, while developing features in components, trying to use mostly the <a href="https://angular.io/" rel="noopener noreferrer external">Angular</a> <code>| async</code> pipe that automatically unsubscribe when components are destroyed, can in addition to avoid memory leak, help improve the coding style.</p>
<p>To explore such modus operandi, let’s refactor an Angular application that mix imperative and declarative programming concepts.</p>
<hr />
<h3>Starting Point</h3>
<p>The following demo uses the <a href="https://api.coinpaprika.com" rel="noopener noreferrer external">coinpaprika API</a> to display a list of cryptocurrencies to its user.</p>
<p>The code source is available on <a href="https://github.com/peterpeterparker/rxjs-no-subscribe" rel="noopener noreferrer external">GitHub</a>. Each following chapters (steps <a href="https://github.com/peterpeterparker/rxjs-no-subscribe/tree/step_1" rel="noopener noreferrer external">1</a>, <a href="https://github.com/peterpeterparker/rxjs-no-subscribe/tree/step_2" rel="noopener noreferrer external">2</a>, <a href="https://github.com/peterpeterparker/rxjs-no-subscribe/tree/step_3" rel="noopener noreferrer external">3</a> and <a href="https://github.com/peterpeterparker/rxjs-no-subscribe/tree/step_4" rel="noopener noreferrer external">4</a>) are separate branches.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*Yw5GBUxaeWBeC8bGIgMPlA.png" alt="" / loading="lazy"></p>
<p>It defers the HTTP queries to a <code>coins.service</code> and presents the results in a <code>coins.component</code>.</p>
<hr />
<h4>Service</h4>
<p>The provider acts as a store. It queries the list of cryptos, filter the results and persists these in memory.</p>
<p>The function <code>list()</code> is both reactive, by telling what it wants (<code>httpClient.get</code>), and imperative by checking and filtering the results.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Injectable, OnDestroy } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { HttpClient } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/common/http</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Subject } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">rxjs</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { takeUntil } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">rxjs/operators</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> type</span><span style="color:#8BE9FD;font-style:italic"> Coin</span><span style="color:#FF79C6"> =</span><span style="color:#8BE9FD;font-style:italic"> Record</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Injectable</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  providedIn</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">root</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> CoinsService</span><span style="color:#FF79C6"> implements</span><span style="color:#8BE9FD;font-style:italic"> OnDestroy</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  constructor</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">private</span><span style="color:#FFB86C;font-style:italic"> httpClient</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HttpClient</span><span style="color:#F8F8F2">) {}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#F8F8F2"> coins</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Coin</span><span style="color:#F8F8F2">[] </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> [];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#F8F8F2"> destroy$</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Subject</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">void</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Subject</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  list</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.httpClient</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Coin</span><span style="color:#F8F8F2">[]>(</span><span style="color:#F1FA8C">`https://api.coinpaprika.com/v1/coins`</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">pipe</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">takeUntil</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.destroy$))</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">subscribe</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">allCoins</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Coin</span><span style="color:#F8F8F2">[]) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (allCoins.length </span><span style="color:#FF79C6">></span><span style="color:#BD93F9"> 10</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">          this</span><span style="color:#F8F8F2">.coins </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> allCoins.</span><span style="color:#50FA7B">filter</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#F8F8F2">            (</span><span style="color:#FFB86C;font-style:italic">coin</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Coin</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span></span>
<span class="line"><span style="color:#FF79C6">              !</span><span style="color:#F8F8F2">coin.is_new </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> coin.rank </span><span style="color:#FF79C6">></span><span style="color:#BD93F9"> 0</span><span style="color:#FF79C6"> &#x26;&#x26;</span><span style="color:#F8F8F2"> coin.rank </span><span style="color:#FF79C6">&#x3C;</span><span style="color:#BD93F9"> 100</span></span>
<span class="line"><span style="color:#F8F8F2">          );</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">      });</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  getCoins</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Coin</span><span style="color:#F8F8F2">[] {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.coins;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  ngOnDestroy</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.destroy$.</span><span style="color:#50FA7B">next</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.destroy$.</span><span style="color:#50FA7B">complete</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h4>Component</h4>
<p>The component initializes the service and exposes a getter binding to parse the results to the UI.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Component, OnInit } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { CoinsService } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">../coins.service</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  selector</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-coins</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  templateUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./coins.component.html</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  styleUrls</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">./coins.component.css</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">]</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> CoinsComponent</span><span style="color:#FF79C6"> implements</span><span style="color:#8BE9FD;font-style:italic"> OnInit</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  constructor</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">private</span><span style="color:#FF79C6"> readonly</span><span style="color:#FFB86C;font-style:italic"> coinsService</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> CoinsService</span><span style="color:#F8F8F2">) {}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  ngOnInit</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> void</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.coinsService.</span><span style="color:#50FA7B">list</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  get</span><span style="color:#50FA7B"> coins</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.coinsService.</span><span style="color:#50FA7B">getCoins</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h4>Template</h4>
<p>The HTML list the coins.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">article</span><span style="color:#50FA7B;font-style:italic"> *ngFor</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">let coin of coins</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">>{{ coin.name }}&#x3C;/</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">>Symbol: {{ coin.symbol }}&#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">>Rank: {{ coin.rank }}&#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">hr</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">article</span><span style="color:#F8F8F2">></span></span></code></pre><hr />
<h3>Step 1: (More) Declarative</h3>
<p>Even though I said above that the rule is actually a guideline, I would suggest anyway to <strong>never</strong> subscribe in services, respectively to be more strict about memory leaks.</p>
<hr />
<p>As we do not want to subscribe, we have first to transform the method that is called by the component to return an <code>Observable</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">list</span><span style="color:#F8F8F2">(): Observable</span><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2">Coin[]</span><span style="color:#FF79C6">></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  return this.httpClient</span></span>
<span class="line"><span style="color:#F8F8F2">    .get&#x3C;Coin[]>(`https</span><span style="color:#FF79C6">:</span><span style="color:#6272A4">//api.coinpaprika.com/v1/coins`)</span></span>
<span class="line"><span style="color:#FF79C6">    ...</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Without any other changes, the compiler will warn you about the return values that are not matching (as we are still subscribing to the stream and therefore are actually returning a <code>Subscription</code>). That’s why, we replace the <code>subscribe</code> with an RxJS operator. In the particular case we are using <a href="https://rxjs.dev/api/operators/tap" rel="noopener noreferrer external">tap</a> because we still want to assign the result to the store.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">list</span><span style="color:#F8F8F2">(): Observable</span><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2">Coin[]</span><span style="color:#FF79C6">></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  return this.httpClient</span></span>
<span class="line"><span style="color:#F8F8F2">    .get&#x3C;Coin[]>(`https</span><span style="color:#FF79C6">:</span><span style="color:#6272A4">//api.coinpaprika.com/v1/coins`)</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">pipe</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#50FA7B">      tap</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">allCoins</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Coin</span><span style="color:#F8F8F2">[]) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (allCoins.length </span><span style="color:#FF79C6">></span><span style="color:#BD93F9"> 10</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">          this</span><span style="color:#F8F8F2">.coins </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> allCoins.</span><span style="color:#50FA7B">filter</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#F8F8F2">            (</span><span style="color:#FFB86C;font-style:italic">coin</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Coin</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span></span>
<span class="line"><span style="color:#FF79C6">              !</span><span style="color:#F8F8F2">coin.is_new </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> coin.rank </span><span style="color:#FF79C6">></span><span style="color:#BD93F9"> 0</span><span style="color:#FF79C6"> &#x26;&#x26;</span><span style="color:#F8F8F2"> coin.rank </span><span style="color:#FF79C6">&#x3C;</span><span style="color:#BD93F9"> 100</span></span>
<span class="line"><span style="color:#F8F8F2">          );</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">      }),</span></span>
<span class="line"><span style="color:#50FA7B">      takeUntil</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.destroy$))</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Because we are now not subscribing anymore, we can remove the <code>takeUntil</code> and let the caller handles the way it streams the data.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Injectable } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { HttpClient } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/common/http</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Observable } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">rxjs</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { tap } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">rxjs/operators</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> type</span><span style="color:#8BE9FD;font-style:italic"> Coin</span><span style="color:#FF79C6"> =</span><span style="color:#8BE9FD;font-style:italic"> Record</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Injectable</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  providedIn</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">root</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> CoinsService</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  constructor</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">private</span><span style="color:#FFB86C;font-style:italic"> httpClient</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HttpClient</span><span style="color:#F8F8F2">) {}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#F8F8F2"> coins</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Coin</span><span style="color:#F8F8F2">[] </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> [];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  list</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Observable</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Coin</span><span style="color:#F8F8F2">[]> {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.httpClient</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Coin</span><span style="color:#F8F8F2">[]>(</span><span style="color:#F1FA8C">`https://api.coinpaprika.com/v1/coins`</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">pipe</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#50FA7B">        tap</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">allCoins</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Coin</span><span style="color:#F8F8F2">[]) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">          if</span><span style="color:#F8F8F2"> (allCoins.length </span><span style="color:#FF79C6">></span><span style="color:#BD93F9"> 10</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">            this</span><span style="color:#F8F8F2">.coins </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> allCoins.</span><span style="color:#50FA7B">filter</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#F8F8F2">              (</span><span style="color:#FFB86C;font-style:italic">coin</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Coin</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span></span>
<span class="line"><span style="color:#FF79C6">                !</span><span style="color:#F8F8F2">coin.is_new </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> coin.rank </span><span style="color:#FF79C6">></span><span style="color:#BD93F9"> 0</span><span style="color:#FF79C6"> &#x26;&#x26;</span><span style="color:#F8F8F2"> coin.rank </span><span style="color:#FF79C6">&#x3C;</span><span style="color:#BD93F9"> 100</span></span>
<span class="line"><span style="color:#F8F8F2">            );</span></span>
<span class="line"><span style="color:#F8F8F2">          }</span></span>
<span class="line"><span style="color:#F8F8F2">        })</span></span>
<span class="line"><span style="color:#F8F8F2">      );</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  getCoins</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Coin</span><span style="color:#F8F8F2">[] {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.coins;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>The code has already become cleaner, no more subscription and destroy lifecycle but, the code is still mixing different approaches. That’s why we take advantages of RxJS <a href="https://rxjs.dev/api/operators/filter" rel="noopener noreferrer external">filter</a> and <a href="https://rxjs.dev/api/operators/map" rel="noopener noreferrer external">map</a> operators to make it more reactive.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">list</span><span style="color:#F8F8F2">(): Observable</span><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2">Coin[]</span><span style="color:#FF79C6">></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  return this.httpClient</span></span>
<span class="line"><span style="color:#F8F8F2">    .get&#x3C;Coin[]>(`https</span><span style="color:#FF79C6">:</span><span style="color:#6272A4">//api.coinpaprika.com/v1/coins`)</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">pipe</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#50FA7B">      filter</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">allCoins</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Coin</span><span style="color:#F8F8F2">[]) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> allCoins.length </span><span style="color:#FF79C6">></span><span style="color:#BD93F9"> 10</span><span style="color:#F8F8F2">),</span></span>
<span class="line"><span style="color:#50FA7B">      map</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">allCoins</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Coin</span><span style="color:#F8F8F2">[]) </span><span style="color:#FF79C6">=></span></span>
<span class="line"><span style="color:#F8F8F2">        allCoins.</span><span style="color:#50FA7B">filter</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#F8F8F2">          (</span><span style="color:#FFB86C;font-style:italic">coin</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Coin</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span></span>
<span class="line"><span style="color:#FF79C6">            !</span><span style="color:#F8F8F2">coin.is_new </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> coin.rank </span><span style="color:#FF79C6">></span><span style="color:#BD93F9"> 0</span><span style="color:#FF79C6"> &#x26;&#x26;</span><span style="color:#F8F8F2"> coin.rank </span><span style="color:#FF79C6">&#x3C;</span><span style="color:#BD93F9"> 100</span></span>
<span class="line"><span style="color:#F8F8F2">        )</span></span>
<span class="line"><span style="color:#F8F8F2">      ),</span></span>
<span class="line"><span style="color:#50FA7B">      tap</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">topCoins</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Coin</span><span style="color:#F8F8F2">[]) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> (</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.coins </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> topCoins))</span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>The imperative <code>if</code> has become a reactive <code>filter</code> and the <code>array.filter</code> has been moved to a <code>map</code> transformer. Thanks to these last modifications the data sources flow through the stream that describe what we want as a results.</p>
<hr />
<h3>Step 2: Subscribe In Component</h3>
<p>Even though the code still compiles, at this point no currencies are displayed anymore because no caller is using, is subscribing, to the stream.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*kNHqMcjFeNKFFOYNAZZMBg.png" alt="" / loading="lazy"></p>
<p>As we are proceeding iteratively, we basically reproduce what we removed in the service earlier, we subscribe within the component.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Component, OnDestroy, OnInit } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Subject } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">rxjs</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { takeUntil } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">rxjs/operators</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { CoinsService } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">../coins.service</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  selector</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-coins</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  templateUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./coins.component.html</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  styleUrls</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">./coins.component.css</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">]</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> CoinsComponent</span><span style="color:#FF79C6"> implements</span><span style="color:#8BE9FD;font-style:italic"> OnInit</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">OnDestroy</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  constructor</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">private</span><span style="color:#FF79C6"> readonly</span><span style="color:#FFB86C;font-style:italic"> coinsService</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> CoinsService</span><span style="color:#F8F8F2">) {}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#F8F8F2"> destroy$</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Subject</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">void</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Subject</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">void</span><span style="color:#F8F8F2">>();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  ngOnInit</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> void</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.coinsService</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">list</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">pipe</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">takeUntil</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.destroy$))</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">subscribe</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {});</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  ngOnDestroy</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.destroy$.</span><span style="color:#50FA7B">next</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.destroy$.</span><span style="color:#50FA7B">complete</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  get</span><span style="color:#50FA7B"> coins</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.coinsService.</span><span style="color:#50FA7B">getCoins</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>I know, I said “never subscribe”, this ain’t the end 😉. Nevertheless, we notice that the cryptos are listed again.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*HB1cJ9j7em8iz4QodbUvHg.png" alt="" / loading="lazy"></p>
<hr />
<h3>Step 3: Async Pipe</h3>
<p>To reach our ultimate goal, we want to remove the subscription in the component in order to leverage the <code>| async</code> pipe. Therefore, we have to improve our service. On the other hand, we still want it to act as a store.</p>
<p>That’s why, as an intermediate step, we replace the imperative state <code>coins</code> of the service with a <a href="https://rxjs.dev/api/index/class/BehaviorSubject" rel="noopener noreferrer external">BehaviorSubject</a>, a special type of Observable that allows values to be multicasted to many Observers (<a href="https://rxjs.dev/guide/subject" rel="noopener noreferrer external">source</a>), and exposes it streams publicly as a <code>readonly Observable</code> variable.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Injectable } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { HttpClient } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/common/http</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { BehaviorSubject, Observable } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">rxjs</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { filter, map, tap } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">rxjs/operators</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> type</span><span style="color:#8BE9FD;font-style:italic"> Coin</span><span style="color:#FF79C6"> =</span><span style="color:#8BE9FD;font-style:italic"> Record</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Injectable</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  providedIn</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">root</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> CoinsService</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  constructor</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">private</span><span style="color:#FFB86C;font-style:italic"> httpClient</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HttpClient</span><span style="color:#F8F8F2">) {}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#F8F8F2"> coins</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> BehaviorSubject</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Coin</span><span style="color:#F8F8F2">[]> </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#F8F8F2"> BehaviorSubject</span><span style="color:#FF79C6">&#x3C;</span></span>
<span class="line"><span style="color:#F8F8F2">    Coin[]</span></span>
<span class="line"><span style="color:#FF79C6">  ></span><span style="color:#F8F8F2">([]);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  readonly</span><span style="color:#F8F8F2"> coins$</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Observable</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Coin</span><span style="color:#F8F8F2">[]> </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.coins.</span><span style="color:#50FA7B">asObservable</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  list</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Observable</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Coin</span><span style="color:#F8F8F2">[]> {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.httpClient</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Coin</span><span style="color:#F8F8F2">[]>(</span><span style="color:#F1FA8C">`https://api.coinpaprika.com/v1/coins`</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">pipe</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#50FA7B">        filter</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">allCoins</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Coin</span><span style="color:#F8F8F2">[]) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> allCoins.length </span><span style="color:#FF79C6">></span><span style="color:#BD93F9"> 10</span><span style="color:#F8F8F2">),</span></span>
<span class="line"><span style="color:#50FA7B">        map</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">allCoins</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Coin</span><span style="color:#F8F8F2">[]) </span><span style="color:#FF79C6">=></span></span>
<span class="line"><span style="color:#F8F8F2">          allCoins.</span><span style="color:#50FA7B">filter</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#F8F8F2">            (</span><span style="color:#FFB86C;font-style:italic">coin</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Coin</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span></span>
<span class="line"><span style="color:#FF79C6">              !</span><span style="color:#F8F8F2">coin.is_new </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> coin.rank </span><span style="color:#FF79C6">></span><span style="color:#BD93F9"> 0</span><span style="color:#FF79C6"> &#x26;&#x26;</span><span style="color:#F8F8F2"> coin.rank </span><span style="color:#FF79C6">&#x3C;</span><span style="color:#BD93F9"> 100</span></span>
<span class="line"><span style="color:#F8F8F2">          )</span></span>
<span class="line"><span style="color:#F8F8F2">        ),</span></span>
<span class="line"><span style="color:#50FA7B">        tap</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">topCoins</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Coin</span><span style="color:#F8F8F2">[]) </span><span style="color:#FF79C6">=></span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.coins.</span><span style="color:#50FA7B">next</span><span style="color:#F8F8F2">(topCoins))</span></span>
<span class="line"><span style="color:#F8F8F2">      );</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>In comparison to our previous changes, this is breaking. That’s why we have to adapt the component to remove the <code>getter</code> and replace it with an observable we can ultimately use in the template.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Component, OnDestroy, OnInit } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Observable, Subject } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">rxjs</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { takeUntil } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">rxjs/operators</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Coin, CoinsService } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">../coins.service</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  selector</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-coins</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  templateUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./coins.component.html</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  styleUrls</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">./coins.component.css</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">]</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> CoinsComponent</span><span style="color:#FF79C6"> implements</span><span style="color:#8BE9FD;font-style:italic"> OnInit</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">OnDestroy</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  constructor</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">private</span><span style="color:#FF79C6"> readonly</span><span style="color:#FFB86C;font-style:italic"> coinsService</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> CoinsService</span><span style="color:#F8F8F2">) {}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#F8F8F2"> destroy$</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Subject</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">void</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Subject</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">void</span><span style="color:#F8F8F2">>();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  coins$</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Observable</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Coin</span><span style="color:#F8F8F2">[]> </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.coinsService.coins$;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  ngOnInit</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> void</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.coinsService</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">list</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">pipe</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">takeUntil</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.destroy$))</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">subscribe</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {});</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  ngOnDestroy</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.destroy$.</span><span style="color:#50FA7B">next</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.destroy$.</span><span style="color:#50FA7B">complete</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Finally, we introduce the famous <code>async</code> pipe.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">article</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">*ngFor="let</span><span style="color:#50FA7B;font-style:italic"> coin</span><span style="color:#50FA7B;font-style:italic"> of</span><span style="color:#50FA7B;font-style:italic"> coins$</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">|</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">async"></span></span></code></pre><hr />
<h3>Step 4: No Subscribe And Reactive</h3>
<p>Our current solution is really close to comply with the goals, we are using a stream to get the data and to display the results but, we still have to subscribe to trigger the loading the currencies.</p>
<p>That’s why we try to remove the subject.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Injectable } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { HttpClient } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/common/http</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Observable } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">rxjs</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { filter, map } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">rxjs/operators</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> type</span><span style="color:#8BE9FD;font-style:italic"> Coin</span><span style="color:#FF79C6"> =</span><span style="color:#8BE9FD;font-style:italic"> Record</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Injectable</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  providedIn</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">root</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> CoinsService</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  constructor</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">private</span><span style="color:#FFB86C;font-style:italic"> httpClient</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HttpClient</span><span style="color:#F8F8F2">) {}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  readonly</span><span style="color:#F8F8F2"> coins$</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Observable</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Coin</span><span style="color:#F8F8F2">[]> </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> ...</span><span style="color:#6272A4"> // &#x3C;- TODO</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  list</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Observable</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Coin</span><span style="color:#F8F8F2">[]> {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.httpClient</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Coin</span><span style="color:#F8F8F2">[]>(</span><span style="color:#F1FA8C">`https://api.coinpaprika.com/v1/coins`</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">pipe</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#50FA7B">        filter</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">allCoins</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Coin</span><span style="color:#F8F8F2">[]) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> allCoins.length </span><span style="color:#FF79C6">></span><span style="color:#BD93F9"> 10</span><span style="color:#F8F8F2">),</span></span>
<span class="line"><span style="color:#50FA7B">        map</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">allCoins</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Coin</span><span style="color:#F8F8F2">[]) </span><span style="color:#FF79C6">=></span></span>
<span class="line"><span style="color:#F8F8F2">          allCoins.</span><span style="color:#50FA7B">filter</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#F8F8F2">            (</span><span style="color:#FFB86C;font-style:italic">coin</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Coin</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span></span>
<span class="line"><span style="color:#FF79C6">              !</span><span style="color:#F8F8F2">coin.is_new </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> coin.rank </span><span style="color:#FF79C6">></span><span style="color:#BD93F9"> 0</span><span style="color:#FF79C6"> &#x26;&#x26;</span><span style="color:#F8F8F2"> coin.rank </span><span style="color:#FF79C6">&#x3C;</span><span style="color:#BD93F9"> 100</span></span>
<span class="line"><span style="color:#F8F8F2">          )</span></span>
<span class="line"><span style="color:#F8F8F2">        )</span></span>
<span class="line"><span style="color:#F8F8F2">      );</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>We notice the exposed observable, <code>coins$</code>, is now lacking a source.</p>
<p>On the other hand, we still have the stream that process the flow of the data as we except.</p>
<p>Yes, that’s right, we connect both.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">readonly coins$: Observable</span><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2">Coin[]</span><span style="color:#FF79C6">></span><span style="color:#FF79C6"> =</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.httpClient</span></span>
<span class="line"><span style="color:#F8F8F2">  .</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Coin</span><span style="color:#F8F8F2">[]>(</span><span style="color:#F1FA8C">`https://api.coinpaprika.com/v1/coins`</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">  .</span><span style="color:#50FA7B">pipe</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#50FA7B">    filter</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">allCoins</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Coin</span><span style="color:#F8F8F2">[]) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> allCoins.length </span><span style="color:#FF79C6">></span><span style="color:#BD93F9"> 10</span><span style="color:#F8F8F2">),</span></span>
<span class="line"><span style="color:#50FA7B">    map</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">allCoins</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Coin</span><span style="color:#F8F8F2">[]) </span><span style="color:#FF79C6">=></span></span>
<span class="line"><span style="color:#F8F8F2">      allCoins.</span><span style="color:#50FA7B">filter</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#F8F8F2">        (</span><span style="color:#FFB86C;font-style:italic">coin</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Coin</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span></span>
<span class="line"><span style="color:#FF79C6">          !</span><span style="color:#F8F8F2">coin.is_new </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> coin.rank </span><span style="color:#FF79C6">></span><span style="color:#BD93F9"> 0</span><span style="color:#FF79C6"> &#x26;&#x26;</span><span style="color:#F8F8F2"> coin.rank </span><span style="color:#FF79C6">&#x3C;</span><span style="color:#BD93F9"> 100</span></span>
<span class="line"><span style="color:#F8F8F2">      )</span></span>
<span class="line"><span style="color:#F8F8F2">    )</span></span>
<span class="line"><span style="color:#F8F8F2">  );</span></span></code></pre><p>However, doing so, we do loose the state management feature we had in place thanks to the use of the BehaviorSubject. That’s why we introduce a <a href="https://rxjs.dev/api/operators/shareReplay" rel="noopener noreferrer external">shareReplay</a> that will also replay values, that will also make our service acts as a store.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Injectable } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { HttpClient } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/common/http</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Observable } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">rxjs</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {filter, map, shareReplay} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">rxjs/operators</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> type</span><span style="color:#8BE9FD;font-style:italic"> Coin</span><span style="color:#FF79C6"> =</span><span style="color:#8BE9FD;font-style:italic"> Record</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Injectable</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  providedIn</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">root</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> CoinsService</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  constructor</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">private</span><span style="color:#FFB86C;font-style:italic"> httpClient</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HttpClient</span><span style="color:#F8F8F2">) {}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  readonly</span><span style="color:#F8F8F2"> coins$</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Observable</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Coin</span><span style="color:#F8F8F2">[]> </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.httpClient</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Coin</span><span style="color:#F8F8F2">[]>(</span><span style="color:#F1FA8C">`https://api.coinpaprika.com/v1/coins`</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">pipe</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#50FA7B">      filter</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">allCoins</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Coin</span><span style="color:#F8F8F2">[]) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> allCoins.length </span><span style="color:#FF79C6">></span><span style="color:#BD93F9"> 10</span><span style="color:#F8F8F2">),</span></span>
<span class="line"><span style="color:#50FA7B">      map</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">allCoins</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Coin</span><span style="color:#F8F8F2">[]) </span><span style="color:#FF79C6">=></span></span>
<span class="line"><span style="color:#F8F8F2">        allCoins.</span><span style="color:#50FA7B">filter</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#F8F8F2">          (</span><span style="color:#FFB86C;font-style:italic">coin</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Coin</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span></span>
<span class="line"><span style="color:#FF79C6">            !</span><span style="color:#F8F8F2">coin.is_new </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> coin.rank </span><span style="color:#FF79C6">></span><span style="color:#BD93F9"> 0</span><span style="color:#FF79C6"> &#x26;&#x26;</span><span style="color:#F8F8F2"> coin.rank </span><span style="color:#FF79C6">&#x3C;</span><span style="color:#BD93F9"> 100</span></span>
<span class="line"><span style="color:#F8F8F2">        )</span></span>
<span class="line"><span style="color:#F8F8F2">      ),</span></span>
<span class="line"><span style="color:#50FA7B">      shareReplay</span><span style="color:#F8F8F2">({ bufferSize</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 1</span><span style="color:#F8F8F2">, refCount</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2"> })</span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><blockquote>
<p>If you never used shareReplay before, be careful when using it. Read more in the <a href="https://blog.strongbrew.io/share-replay-issue/" rel="noopener noreferrer external">blog post</a> of Kwinten Pisman.</p>
</blockquote>
<p>Finally, we can remove our last subscription in the component and also all linked code that has for goal to handle the un-subscription.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Component } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Observable } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">rxjs</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Coin, CoinsService } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">../coins.service</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  selector</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-coins</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  templateUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./coins.component.html</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  styleUrls</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">./coins.component.css</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">]</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> CoinsComponent</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  constructor</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">private</span><span style="color:#FF79C6"> readonly</span><span style="color:#FFB86C;font-style:italic"> coinsService</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> CoinsService</span><span style="color:#F8F8F2">) {}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  readonly</span><span style="color:#F8F8F2"> coins$</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Observable</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Coin</span><span style="color:#F8F8F2">[]> </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.coinsService.coins$;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>If you compare to its original version, has not the component become really slim and easy to understand?</p>
<p>A last check to the GUI.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*HB1cJ9j7em8iz4QodbUvHg.png" alt="" / loading="lazy"></p>
<p>All cryptos are still listed, the code is reactive and, we are not using any “subscribe” anymore 🥳.</p>
<hr />
<h3>Summary</h3>
<p>Trying to not subscribe using RxJS in Angular is not a finality nor a strict rule but, when applied as a guideline, can help make the code cleaner and reactive, can help with the experience and time get better at RxJS.</p>
<p>To infinity and beyond!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Internet Computer: Web App Decentralized Database Architecture]]></title>
          <description><![CDATA[An approach of the data persistence for web applications with the Internet Computer of the DFINITY foundation.]]></description>
          <link>https://daviddalbusco.com/blog/internet-computer-web-app-decentralized-database-architecture/</link>
          <pubDate>Tue, 17 Aug 2021 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*eyGnrhxXL92xcbpJv4DqAA.jpeg" alt="" / loading="lazy"></p>
<p>We are developing a proof of concept to port our web app, <a href="https://deckdeckgo.com/" rel="noopener noreferrer external">DeckDeckGo</a>, to <a href="https://dfinity.org" rel="noopener noreferrer external">DFINITY's Internet Computer</a>.</p>
<p>After having validated the hosting and password-less authentication integration, we are tackling the last question of our POC: persisting user data and presentations in the blockchain.</p>
<p>Along the way, we tried out two concepts:</p>
<ul>
<li>a “conservative” one: a data persistence in a single database-like storage</li>
<li>a “futuristic 🤯” one: generate a database-like smart contract on the fly for each deck created by a user.</li>
</ul>
<p>In this article I present these two approaches.</p>
<hr />
<h3>Introduction</h3>
<p>The scope of this blog post is limited to simple key-value database concepts. That kind of persistence is the one we rely on in DeckDeckGo.</p>
<p>I do not know if anyone has already implemented heavier concepts with the IC (Internet Computer), such as a fully functional SQL database running on the blockchain.</p>
<p>You can of course challenge these by commenting on the blog post or by reaching out 😃.</p>
<hr />
<h3>The “Nowadays”</h3>
<p>Before digging into the Internet Computer, let's first review some common, old fashion concepts.</p>
<p>Nowadays, server-less or not, the data persistence is often solve with one database for all information.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*eWuHlblTCJMdrJVNllri9Q.png" alt="" / loading="lazy"></p>
<p>Users use the (web) application. It calls endpoint(s) to persist and read data and, it returns the results to the users.</p>
<p>In a microservice architecture, the application(s) is split as a collection of services but, to some extension, the outcome is based on the same concept.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*3mAmSM4_l4QDF0aK2I_esA.png" alt="" / loading="lazy"></p>
<p>Users use the (web) application. It calls a dedicated service which in turn calls endpoint(s) to persist and read data and, returns the results to the users.</p>
<p>Regardless of these architectures, mono- or micro-services, the data is stored in a monolithic database.</p>
<hr />
<h4>Code Snippet</h4>
<p>I do not know the exact architecture of Google <a href="https://firebase.google.com/docs/firestore/quickstart" rel="noopener noreferrer external">Firestore</a> but, from a user and outside point of view, it looks like a mono-database hosted in the cloud.</p>
<p>If we develop, for example, an application that lists the kind of animals our users own, we would most probably define a db collection of <code>pets</code> to collect their entries.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*I4CP0N8GvViWCuAzRLa3UA.png" alt="" / loading="lazy"></p>
<p>To query and persist the data, we would then implement getter (<code>get</code> ) and setter (<code>add</code> ) functions in our web application that would call the cloud database.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> firebase </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">firebase/app</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">firebase/firestore</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> firestore </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> firebase.</span><span style="color:#50FA7B">firestore</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> get</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">entryId</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> snapshot </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> firestore.</span><span style="color:#50FA7B">collection</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">pets</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">).</span><span style="color:#50FA7B">doc</span><span style="color:#F8F8F2">(entryId).</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (snapshot.exists) {</span></span>
<span class="line"><span style="color:#F8F8F2">        console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(snapshot.</span><span style="color:#50FA7B">data</span><span style="color:#F8F8F2">());</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> add</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">userId</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">dog</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">cat</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { id</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> entryId } </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> firestore.</span><span style="color:#50FA7B">collection</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">pets</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">).</span><span style="color:#50FA7B">add</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        userId,</span></span>
<span class="line"><span style="color:#F8F8F2">        dog,</span></span>
<span class="line"><span style="color:#F8F8F2">        cat</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(entryId);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>All users would use the same entry point and their data would also be saved in the same collection (<code>firestore.collection('pets')</code> ).</p>
<hr />
<h3>The “Conservative”</h3>
<p>The Internet Computer does not provide a built-in, just-add-water database solution, that is not the goal of the DFINITY foundation (my understanding). They provide a futuristic blockchain network, that can run web applications too. That's &quot;all&quot;.</p>
<p>It is up to the makers to build features on top of it. They can implement features and unleash these on the IC through canister smart contracts.</p>
<p>I picture a &quot;canister&quot; as a container that runs a set of functions in the cloud, except that it is actually a decentralized blockchain network 😉. It can basically do anything that can be implemented, from computing to data persistence.</p>
<p>After studying the <a href="https://sdk.dfinity.org/docs/introduction/welcome.html" rel="noopener noreferrer external">documentation</a> of the SDK, <a href="https://github.com/dfinity/examples" rel="noopener noreferrer external">examples</a> and the “<a href="https://github.com/dfinity/linkedup/" rel="noopener noreferrer external">linkedup</a>” demo, our first approach had then for goal to replicate the approach we are familiar with, i.e. the “Nowadays&quot; or &quot;monolithic&quot; database approach.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*GK30d2fOgC-7UVWdAnrQFA.png" alt="" / loading="lazy"></p>
<p>However, given the blockchain nature of the Internet Computer, it may be good to imagine the architecture in a decentralized way with data shared in the form of blocks.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*jI-CDFohigqIZCibwWrcqw.png" alt="" / loading="lazy"></p>
<p>If we omit the blockchain aspect (and the decentralization), it basically works the same as what I am familiar with, doesn't it?</p>
<hr />
<h4>Code Snippet</h4>
<p>The way we can picture the persistence of the data within a canister, like we would for our &quot;pets&quot; web application, would be again quite similar to the previous architecture.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*DcChK7ojaMYGqExH6F-YtQ.png" alt="" / loading="lazy"></p>
<p>Instead of a key-value database collection -- as there are no built-in databases available -- our actor would simply have an attribute holding the data.</p>
<p>That attribute can for example be a <a href="https://sdk.dfinity.org/docs/base-libraries/trie" rel="noopener noreferrer external">Trie</a>, a functional map (and set) whose representation is “canonical”, and independent of operation history.</p>
<p>To query (<code>get</code> ) and persist (<code>add</code> ) the data, we would be able to implement an actor, a canister, as following:</p>
<p><em>Canisters and features unleashed in the Internet Computer can commonly either be written in <a href="https://sdk.dfinity.org/docs/language-guide/motoko.html" rel="noopener noreferrer external">Motoko</a> or <a href="https://www.rust-lang.org" rel="noopener noreferrer external">Rust</a>. Motoko is published and maintained by the DFINITY foundation and most of the documentation’s examples are provided in that particular programming language. That is mostly why we developed our proof of concept with Motoko.</em></p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> Trie </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">mo:base/Trie</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">import Nat32 </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">mo:base/Nat32</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">actor {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    type</span><span style="color:#F8F8F2"> PetId = Nat32;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    type</span><span style="color:#F8F8F2"> Data = {</span></span>
<span class="line"><span style="color:#F8F8F2">        dog: Bool;</span></span>
<span class="line"><span style="color:#F8F8F2">        cat: Bool;</span></span>
<span class="line"><span style="color:#F8F8F2">        userId: Text;</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    private stable var entryId: PetId = 0;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    private stable var pets : Trie.Trie&#x3C;PetId, Data> = Trie.empty();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    public func add(userId: Text, dog: Bool, cat: Bool): async () {</span></span>
<span class="line"><span style="color:#F8F8F2">        let data = {</span></span>
<span class="line"><span style="color:#F8F8F2">            userId = userId;</span></span>
<span class="line"><span style="color:#F8F8F2">            dog = dog;</span></span>
<span class="line"><span style="color:#F8F8F2">            cat = cat;</span></span>
<span class="line"><span style="color:#F8F8F2">        };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        pets := Trie.replace(</span></span>
<span class="line"><span style="color:#F8F8F2">            pets,</span></span>
<span class="line"><span style="color:#F8F8F2">            key(entryId),</span></span>
<span class="line"><span style="color:#F8F8F2">            Nat32.equal,</span></span>
<span class="line"><span style="color:#F8F8F2">            ?data,</span></span>
<span class="line"><span style="color:#F8F8F2">        ).0;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        entryId += 1;</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    public query func get(entryId: Nat32) : async ?Data {</span></span>
<span class="line"><span style="color:#F8F8F2">        let result = Trie.find(pets, key(entryId), Nat32.equal);</span></span>
<span class="line"><span style="color:#F8F8F2">        return result;</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    private func key(x : PetId) : Trie.Key&#x3C;PetId> {</span></span>
<span class="line"><span style="color:#F8F8F2">        return { hash = x; key = x };</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>The data would be saved in the canister smart-contract, replicated and decentralized, while using an approach we are familiar with.</p>
<p><em>Note: <a href="https://m7sm4-2iaaa-aaaab-qabra-cai.raw.ic0.app/?tag=1638333901" rel="noopener noreferrer external">Link</a> to related Motoko Playground to try and play with above example.</em></p>
<hr />
<h3>The “Futuristic 🤯”</h3>
<p>Our first &quot;Conservative&quot; approach validated the hypothesis that data persistence for our web application in the Internet Compute could be done. But what about scalability?</p>
<p>In our first approach, we did not use a <code>Trie</code> as in the example; instead we store the data in a <code>HashMap</code>. That way, it would have made the system a bit more scalable, as data would have potentially been still delivered more quickly, even with a large amount of data. However, at some point, we might have hit some limits anyway.</p>
<p>In addition, a canister can only store 4GB of memory pages due to the limitations of WebAssembly implementations (<a href="https://medium.com/dfinity/a-technical-overview-of-the-internet-computer-f57c62abc20f" rel="noopener noreferrer external">source</a>).</p>
<p>That is why, we challenged our first idea and, and tried to find a more scalable design.</p>
<p>It resulted in an architecture in which a main actor acts as a a manager that -- on the fly, upon object creation -- generates a decentralized secure simple key-value database-like for each single data persistence of each user 🤯.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*Dnw7c5hNpF8cm49nCmT3JA.png" alt="" / loading="lazy"></p>
<p>In the above diagram I displayed only two users and, did not reflect the blockchain nature of the network but, hopefully, it pictures well the idea.</p>
<p>A user, through the web app, requests the manager to get him/her a personal decentralized secure simple key-value data persistence canister. Once obtained, he/she uses this dedicated private space to query and persist his/her data.</p>
<hr />
<h4>Code Snippet</h4>
<p>We would need two actors to implement such a solution. One that acts as a “manager”, the canister that generates other canisters on the fly, and an implementation for the canisters that take care of the data persistence, those that are generated on the fly.</p>
<p>The “manager” might have to track or not the list of canisters it has generated, unless end users save on their side their references. However, in the following snippet we will assume it does have to keep track of what it generates.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*i1UedTMw4GxvoiwkQI9gvw.png" alt="" / loading="lazy"></p>
<p>The base implementation of the manager is close to what we already implemented. However, instead of <code>Trie</code>, I used a <a href="https://sdk.dfinity.org/docs/base-libraries/hashmap" rel="noopener noreferrer external">HashMap</a> to keep track of the IDs of the canisters that has been generated.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> HashMap </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">mo:base/HashMap</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">import Principal </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">mo:base/Principal</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">import Cycles </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">mo:base/ExperimentalCycles</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">import Iter </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">mo:base/Iter</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">import Pet </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">./Pet</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">actor {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  type</span><span style="color:#F8F8F2"> CanisterId = Principal;</span></span>
<span class="line"><span style="color:#FF79C6">  type</span><span style="color:#F8F8F2"> UserId = Principal;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  private func isPrincipalEqual(x: Principal, y: Principal): Bool {</span></span>
<span class="line"><span style="color:#F8F8F2">    x == y</span></span>
<span class="line"><span style="color:#F8F8F2">  };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  private var canisters: HashMap.HashMap&#x3C;UserId, CanisterId> =</span></span>
<span class="line"><span style="color:#F8F8F2">    HashMap.HashMap&#x3C;UserId, CanisterId>(10, isPrincipalEqual,</span></span>
<span class="line"><span style="color:#F8F8F2">                                        Principal.hash);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  private stable var upgradeCanisters: [(Principal, CanisterId)] =</span></span>
<span class="line"><span style="color:#F8F8F2">    [];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  public shared({caller}) func create(): async (CanisterId) {</span></span>
<span class="line"><span style="color:#F8F8F2">    Cycles.add(1_000_000_000_000);</span></span>
<span class="line"><span style="color:#F8F8F2">    let canister = await Pet.Pet();</span></span>
<span class="line"><span style="color:#F8F8F2">    let id: CanisterId = await canister.id();</span></span>
<span class="line"><span style="color:#F8F8F2">    canisters.put(caller, id);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    return id;</span></span>
<span class="line"><span style="color:#F8F8F2">  };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  public shared query({caller}) func get() : async ?CanisterId {</span></span>
<span class="line"><span style="color:#F8F8F2">    let id: ?CanisterId = canisters.get(caller);</span></span>
<span class="line"><span style="color:#F8F8F2">    return id;</span></span>
<span class="line"><span style="color:#F8F8F2">  };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  system func preupgrade() {</span></span>
<span class="line"><span style="color:#F8F8F2">    upgradeCanisters := Iter.toArray(canisters.entries());</span></span>
<span class="line"><span style="color:#F8F8F2">  };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  system func postupgrade() {</span></span>
<span class="line"><span style="color:#F8F8F2">    canisters := HashMap.fromIter&#x3C;UserId, CanisterId></span></span>
<span class="line"><span style="color:#F8F8F2">                  (upgradeCanisters.vals(), 10,</span></span>
<span class="line"><span style="color:#F8F8F2">                  isPrincipalEqual, Principal.hash);</span></span>
<span class="line"><span style="color:#F8F8F2">                  upgradeCanisters := [];</span></span>
<span class="line"><span style="color:#F8F8F2">  };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>The function <code>create</code> takes care of unleashing new users' canisters in the Internet Computer, those that will be used for the data persistence. It first allocates a minimal cycle count for the new actors.</p>
<p>The <code>get</code> function returns, if it exists, the ID of the user’s canister.</p>
<p>Both <code>preupgrade</code> and <code>postupgrade</code> are used to persist the memory between upgrades (see <a href="https://sdk.dfinity.org/docs/language-guide/upgrades.html" rel="noopener noreferrer external">documentation</a>).</p>
<p>Once the manager developed, we would be able to implement an <code>actor</code> dedicated to the personal data persistence of a user.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> Principal </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">mo:base/Principal</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">actor class Pet() = this {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  type</span><span style="color:#F8F8F2"> Data = {</span></span>
<span class="line"><span style="color:#F8F8F2">    dog: Bool;</span></span>
<span class="line"><span style="color:#F8F8F2">    cat: Bool;</span></span>
<span class="line"><span style="color:#F8F8F2">  };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  private stable var myPet: ?Data = null;</span></span>
<span class="line"><span style="color:#F8F8F2">  public func set(dog: Bool, cat: Bool): async () {</span></span>
<span class="line"><span style="color:#F8F8F2">    myPet := ?{</span></span>
<span class="line"><span style="color:#F8F8F2">      dog = dog;</span></span>
<span class="line"><span style="color:#F8F8F2">      cat = cat;</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"><span style="color:#F8F8F2">  };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  public query func get() : async ?Data {</span></span>
<span class="line"><span style="color:#F8F8F2">    return myPet;</span></span>
<span class="line"><span style="color:#F8F8F2">  };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  public query func id() : async Principal {</span></span>
<span class="line"><span style="color:#F8F8F2">    return Principal.fromActor(this);</span></span>
<span class="line"><span style="color:#F8F8F2">  };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Its implementation looks familiar to what we have seen before, except for a small (but important) modification: there is no <code>entryId</code> anymore and, there is no <code>Trie</code> neither.</p>
<p>Each user has his/her own little kingdom. That's why he/she does not need an identifier to lookup the data in a big pot of data. It doesn't even have to memorize the <code>userId</code>.</p>
<p>It is the user's kingdom, it contains only the personal data of that particular user!</p>
<p><img src="https://daviddalbusco.com/assets/images/1*LfdwUX1sVqLI9zp0WSWO_w.png" alt="" / loading="lazy"></p>
<p><em>Note: <a href="https://m7sm4-2iaaa-aaaab-qabra-cai.raw.ic0.app/?tag=4031577968" rel="noopener noreferrer external">Link</a> to related Motoko Playground to check out above example.</em></p>
<hr />
<h3>Pros And Cons</h3>
<p>The ultimate architecture is really scalable, as every user operates in his/her own realm. It also separates the concern in clear ownerships and fits well a secure approach.</p>
<p>However, it is worth noting that it is linked with more administration, whether during upgrade of their codes or handling their costs.</p>
<p>Nevertheless, we are big fans of this architecture and think that the advantages are totally worth it. Therefore, it is the concept we would use if our proof of concept would turn into a real live productive use case.</p>
<p>After all, great power comes with great responsibility!</p>
<hr />
<h3>Summary</h3>
<p>It is quite a tricky subject. I hope I was able to communicate the main idea behind these architectures and, most importantly, behind the one I called the “Futuristic one 🤯”.</p>
<p>To infinity and beyond!</p>
<p>David</p>
<hr />
<h3>Grant Program</h3>
<p>We are incredibly lucky to have been selected for DFINITY <a href="https://dfinity.org/grants/#utm_source=home&amp;utm_medium=banner&amp;utm_campaign=grants" rel="noopener noreferrer external">Developer Grant Program</a> to support the developer ecosystem, award teams to build dapps, tooling, and infrastructure on the Internet Computer.</p>
<p>I cannot stress enough the fact that if you have a great project you should definitely apply. It has been so far nothing less than an amazing experience and, just give the feeling to take part to something that is ahead of its time.</p>
<hr />
<p>All figures of this article have been developed with <a href="https://excalidraw.com/" rel="noopener noreferrer external">Excalidraw</a>, what a slick drawing tool.</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Singleton & Factory Patterns With TypeScript]]></title>
          <description><![CDATA[The singleton and factory design pattern implemented with TypeScript, explained with a hairdresser and an ice creams shop examples.]]></description>
          <link>https://daviddalbusco.com/blog/singleton-and-factory-patterns-with-typescript/</link>
          <pubDate>Mon, 26 Jul 2021 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*0XVEe8Cqa9xFGveX8JLMxA.jpeg" alt="" / loading="lazy"></p>
<p><em><a href="https://www.zentrale.ch/" rel="noopener noreferrer external">Eisvogel</a> — The best ice creams in Zürich</em></p>
<p>Of all design patterns I learned in engineering school, the singleton and factory are probably those I use the most in my day to day programming activities. Sometimes I even mix the two to double the fun 😜.</p>
<p>In this blog post, I will show you how to implement these patterns with <a href="https://www.typescriptlang.org/" rel="noopener noreferrer external">TypeScript</a>.</p>
<hr />
<h3>Singleton</h3>
<blockquote>
<p>In software engineering, the singleton pattern is a software design pattern that restricts the instantiation of a class to one “single” instance (<a href="https://en.wikipedia.org/wiki/Singleton_pattern" rel="noopener noreferrer external">source</a>).</p>
</blockquote>
<p>A singleton is probably like your hairdresser. When you need an haircut, you do not want to go to a salon and get any new professional, even though she/he shares the same skills as yours but, you exactly want yours because she/he knows already your favorite settings ✂️.</p>
<p>Such a pattern can be implemented by defining the <code>constructor</code> of a <code>class</code> as <code>private</code>, making it de facto not accessible outside the class declaration, and by providing only one single instance of the object which have been made <code>static</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> Hairdresser</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#FF79C6"> static</span><span style="color:#F8F8F2"> instance</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Hairdresser</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> =</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#FF79C6"> constructor</span><span style="color:#F8F8F2">() {}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  static</span><span style="color:#50FA7B"> getInstance</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Hairdresser</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">Hairdresser.instance) {</span></span>
<span class="line"><span style="color:#F8F8F2">      Hairdresser.instance </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Hairdresser</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> Hairdresser.instance;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Using the above snippet, we are not able to get any new hairdresser. Trying to instantiate a new object lead to an error.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">// TS2673: Constructor of class 'Hairdresser' is private and only accessible within the class declaration.</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> hairdresser</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Hairdresser</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Hairdresser</span><span style="color:#F8F8F2">();</span></span></code></pre><p>To the contrary, accessing the instance will always return the first object which has been initialized.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> hairdresser</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Hairdresser</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> Hairdresser.</span><span style="color:#50FA7B">getInstance</span><span style="color:#F8F8F2">();</span></span></code></pre><hr />
<h3>Factory</h3>
<blockquote>
<p>In class-based programming, the factory method pattern is a creational pattern that uses factory methods to deal with the problem of creating objects without having to specify the exact class of the object that will be created. This is done by creating objects by calling a factory method rather than by calling a constructor (<a href="https://en.wikipedia.org/wiki/Factory_method_pattern" rel="noopener noreferrer external">source</a>).</p>
</blockquote>
<p>The factory pattern is like <a href="https://www.gaultmillau.ch/zuri-isst/eisvogel-das-beste-glace-zurich" rel="noopener noreferrer external">Eisvogel</a>, my favorite and of course the best ice creams shop in Zürich. They sell artisanal yummy ice creams they manufacture on a daily basis with five new different flavors a day.</p>
<p>When you get there, you know you are going to get an ice cream but, you do not know which flavor.</p>
<p>In other words, you can use a factory to get objects that share an expected behavior but, which can be implemented differently.</p>
<p>First, it either needs an <code>interface</code> or an <code>abstract</code> class which describes the common behavior in addition to the effective implementation of the expected object which should be created by the factory.</p>
<p>With <code>interface</code> :</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> interface</span><span style="color:#8BE9FD;font-style:italic"> IceCream</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">  yummy</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> Strawberry</span><span style="color:#FF79C6"> implements</span><span style="color:#8BE9FD;font-style:italic"> IceCream</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">  yummy</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> Chocolate</span><span style="color:#FF79C6"> implements</span><span style="color:#8BE9FD;font-style:italic"> IceCream</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">  yummy</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>With <code>abstract</code> (note: using the decorator <code>override</code>, newly introduced with TypeScript 4.3, that indicates a method overrides the parent definition):</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> abstract</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> IceCream</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  abstract</span><span style="color:#50FA7B"> yummy</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> Strawberry</span><span style="color:#FF79C6"> extends</span><span style="color:#8BE9FD;font-style:italic"> IceCream</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  override</span><span style="color:#50FA7B"> yummy</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> Chocolate</span><span style="color:#FF79C6"> extends</span><span style="color:#8BE9FD;font-style:italic"> IceCream</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  override</span><span style="color:#50FA7B"> yummy</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Regardless of the above implementation, a related factory can be implemented. It can be in the form of a <code>static</code> method or a stateless <code>function</code>. The important thing is that depending on a parameter the desired implementation is created and returned.</p>
<p>With a <code>static</code> method:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> Factory</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  static</span><span style="color:#50FA7B"> getIceCream</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> IceCream</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> Math.</span><span style="color:#50FA7B">random</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">%</span><span style="color:#BD93F9"> 2</span><span style="color:#FF79C6"> ===</span><span style="color:#BD93F9"> 0</span><span style="color:#FF79C6"> ?</span></span>
<span class="line"><span style="color:#FF79C6;font-weight:bold">                new</span><span style="color:#50FA7B"> Strawberry</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">:</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Chocolate</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>With a <code>function</code> :</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> getIceCream</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> IceCream</span><span style="color:#FF79C6"> =></span></span>
<span class="line"><span style="color:#F8F8F2">  Math.</span><span style="color:#50FA7B">random</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">%</span><span style="color:#BD93F9"> 2</span><span style="color:#FF79C6"> ===</span><span style="color:#BD93F9"> 0</span><span style="color:#FF79C6"> ?</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Strawberry</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">:</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Chocolate</span><span style="color:#F8F8F2">();</span></span></code></pre><p>I used a <code>random</code> number to create either one or the other type of object. In a real world applications, the variable is often either a parameter of the factory or another option.</p>
<p>Requesting an object through the <code>static</code> method or the <code>function</code> will return different objects.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">// Static method call</span></span>
<span class="line"><span style="color:#F8F8F2">console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(Factory.</span><span style="color:#50FA7B">getIceCream</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">yummy</span><span style="color:#F8F8F2">());</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// Function call</span></span>
<span class="line"><span style="color:#F8F8F2">console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">getIceCream</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">yummy</span><span style="color:#F8F8F2">());</span></span></code></pre><hr />
<h3>Combined</h3>
<p>As I said in my introduction, sometimes I like to double the fun and combine the two patterns.</p>
<p>For example, in <a href="https://github.com/deckgo/deckdeckgo/" rel="noopener noreferrer external">DeckDeckGo</a>, I developed such a concept to get different services according the environment (“local, staging or production”).</p>
<p>Applied to the above <code>IceCream</code> snippet, it would mean that the <code>factory</code> would need to keep track of the object created with a <code>singleton</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> SingletonFactory</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#FF79C6"> static</span><span style="color:#F8F8F2"> instance</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> IceCream</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> =</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  static</span><span style="color:#50FA7B"> getInstance</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> IceCream</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.instance) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">      this</span><span style="color:#F8F8F2">.instance </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#F8F8F2">        Math.</span><span style="color:#50FA7B">random</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">%</span><span style="color:#BD93F9"> 2</span><span style="color:#FF79C6"> ===</span><span style="color:#BD93F9"> 0</span><span style="color:#FF79C6"> ?</span></span>
<span class="line"><span style="color:#FF79C6;font-weight:bold">             new</span><span style="color:#50FA7B"> Strawberry</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">:</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Chocolate</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.instance;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>Summary</h3>
<p>There are many <a href="https://en.wikipedia.org/wiki/Software_design_pattern" rel="noopener noreferrer external">patterns</a> but, these are those I use the most often. Next time I use another type, I should probably bookmark it, it might be worth a new article 😉.</p>
<p>Meanwhile, treat yourself with a yummy ice-cream of Eisvogel next time you visit Zürich 🍦.</p>
<p>To infinity and beyond!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[We Received A Grant To Port Our Web App To The Internet Computer]]></title>
          <description><![CDATA[Can our web editor for slides work on the futuristic internet of the DFINITY foundation? Let's figure it out.]]></description>
          <link>https://daviddalbusco.com/blog/we-received-a-grant-to-port-our-web-app-to-the-internet-computer/</link>
          <pubDate>Tue, 06 Jul 2021 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*uMo-wgL0-asMeYajl6CSfQ.png" alt="" / loading="lazy"></p>
<p><em>Source: DFINITY press kit + DeckDeckGo logo</em></p>
<hr />
<p>Exciting news, we received a 25k USD grant from the <a href="https://dfinity.org/" rel="noopener noreferrer external">DFINITY</a> foundation to port our web editor for slides, <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>, to the <a href="https://dfinity.org/faq/what-is-the-internet-computer" rel="noopener noreferrer external">Internet Computer</a>.</p>
<p>In the coming weeks we will build a proof of concept to validate the feasibility of the project. Here are some insights about the scope, goal and milestones of this inspiring project.</p>
<hr />
<h3>DeckDeckGo</h3>
<p>DeckDeckGo is a web <a href="https://github.com/deckgo/deckdeckgo/" rel="noopener noreferrer external">open source</a> editor for slides. Unlike other tools, presentations can be published and viewed online or shared as Progressive Web Apps (PWA). The editor works on all devices (laptop, mobile, etc.), decks can be remotely controlled and interactions with the audience through live polls are also supported.</p>
<p>DeckDeckGo is a side project that my friend <a href="https://www.nmattia.com" rel="noopener noreferrer external">Nicolas</a> and <a href="https://daviddalbusco.com/" rel="noopener noreferrer external">I</a> - together with our community (2000+ registered users) and help of our contributors - have been working on for the last 2.5 years. Here are our values: open source, transparency and knowledge sharing.</p>
<hr />
<h3>DFINITY</h3>
<p>The DFINITY Foundation is a not-for-profit scientific research organization based in Zurich, Switzerland, that oversees research centers around the globe. The Foundation’s mission is to build, promote, and maintain the Internet Computer. Its team is committed to building advanced experimental technologies to improve the public internet (<a href="https://dfinity.org/faq" rel="noopener noreferrer external">source</a>).</p>
<hr />
<h3>Internet Computer</h3>
<p>The Internet Computer extends the functionality of the public Internet so that it can host backend software, transforming it into a global compute platform.</p>
<p>It seeks to address serious long-standing problems that bedevil IT, including system security, and to provide a means to reverse and undo the ever increasing monopolization of internet services, user relationships and data, and restore the Internet to its permissionless, innovative and creative roots.</p>
<p>The Internet Computer is formed by an advanced decentralized protocol called ICP (Internet Computer Protocol) that independent data centers around the world run to combine the power of individual computers into an unstoppable seamless universe where internet native software is hosted and run with the same security guarantees as smart contracts (<a href="https://dfinity.org/faq" rel="noopener noreferrer external">source</a>).</p>
<hr />
<h4>In Other Words</h4>
<p>The description above is the official one but, let me try to synthesize my understanding:</p>
<p>The Internet Computer is a decentralized blockchain network on which you can run smart contracts that can host web applications. To my mum I would even say it is like bitcoin and cloud providers had a baby. In that way, you get best of both worlds and therefore open a new whole area of possibilities, what DFINITY calls the &quot;Blockchain Singularity&quot;. In addition, they aim to try to challenge the monopolistic position of the <a href="https://en.wikipedia.org/wiki/Big_Tech" rel="noopener noreferrer external">GAFAM</a>.</p>
<hr />
<h3>Goal</h3>
<p>At this stage, our one and only goal is to test the feasibility of the concept: can our platform run and interact** with the Internet Computer? Even if we are really optimistic and everything we read and tried so far seems to validate the hypothesis, the ICP and its infrastructure are still young, it has “only” been officially launched a few weeks ago, on May 7th.</p>
<p>By run* I mean can we host our Progressive Web Apps, our editor, on the Internet Computer and by interact** I mean can it persist and read data, the decks and slides of the users.</p>
<hr />
<h3>Milestones</h3>
<p>To fulfill the proof of concept, and to get our grant 😜, we have defined the following three milestones which must be completed to be able to decide if yes, or no, DeckDeckGo can be migrated to the Internet Computer.</p>
<hr />
<h4>Milestone 1 — Offline First &amp; Hosting</h4>
<p>Intrinsically and because it is still young too, we won’t be able to perform as much HTTP queries as we currently do, because they might be, at least for the time being, a bit slower than our current solution with Firestore. That’s why, we will have to approach the persistence of the data differently.</p>
<p>After some brainstorming, we decided that the most sustainable solution would be to transition from an online first to an offline first strategy. We are going to convert our editor to work primarily with IndexedDB and, to optionally replicate these data to the cloud, ultimately to the Internet Computer.</p>
<p>This also will have the advantage to let anyone use our editor in standalone mode, for example with a Docker container, and will also make more sense for those getting our application through the <a href="https://play.google.com/store/apps/details?id=com.deckdeckgo.twa&amp;hl=fr&amp;gl=US" rel="noopener noreferrer external">Google Play</a> or <a href="https://www.microsoft.com/en-us/p/deckdeckgo/9pfr8n0r5gpp?activetab=pivot:overviewtab" rel="noopener noreferrer external">Microsoft</a> stores.</p>
<p>Finally, in the same challenging milestone, we also aim to deploy our Progressive Web Apps as a static asset on the Internet Computer.</p>
<hr />
<h4>Milestone 2 — Authentication</h4>
<p>The Internet Computer introduces a new secure method of cryptographic authentication that eliminates the ability for service providers to steal data, or track movements (<a href="https://dfinity.org/technicals/web-authentication-identity" rel="noopener noreferrer external">source</a>).</p>
<p>In short: no more passwords at all 🤯!</p>
<p>That is something we are eager to try, because we're super curious about it and because it does sound super cool.</p>
<p>Of course, from a project perspective, we do need an authentication workflow to persist the users’ data on the network. That’s the cherry on top of the fun 😉.</p>
<hr />
<h4>Milestone 3 — Data Persistence</h4>
<p>Unlike the common web solution to query data through HTTP, there is no such function as <code>fetch</code> on the Internet Computer.</p>
<p>The Internet Computer is primarily a distributed and decentralized platform for running software. Therefore, to persist data and run programs on it, the applications have to be deployed as WebAssembly modules. These are executed inside of a conceptual computational unit called a software canister.</p>
<p>Once deployed, end-users can interact with the software canister by accessing the entry point functions defined for that canister through a front-end client such as a browser (<a href="https://sdk.dfinity.org/docs/developers-guide/concepts/canisters-code.html" rel="noopener noreferrer external">source</a>).</p>
<p>Summarized: We will have to write some WebAssembly functions to get and set our data in the Internet Computer. From our editor developed with <a href="https://stenciljs.com/" rel="noopener noreferrer external">StencilJS</a>, we will then interact with these through async callbacks.</p>
<hr />
<h3>Keep In Touch</h3>
<p>To follow our adventure, you can star and watch our <a href="https://github.com/deckgo/deckdeckgo" rel="noopener noreferrer external">GitHub repo</a> ⭐️, join our <a href="https://deckdeckgo.com/en/newsletter" rel="noopener noreferrer external">newsletter</a> or <a href="https://join.slack.com/t/deckdeckgo/shared_invite/enQtNzM0NjMwOTc3NTI0LTBlNmFhODNhYmRkMWUxZmU4ZTQ2MDJiNjlmYWZiODNjMDU5OGRjYThlZmZjMTc5YmQ3MzUzMDlhMzk0ZDgzMDY" rel="noopener noreferrer external">Slack</a> channel. We will share some updates following our progress and, as we often do, some of our learning.</p>
<hr />
<h3>Conclusion</h3>
<p>I play it cool but, I am fully aware it is quite a tremendous opportunity and challenging project. Time to hack!</p>
<p>To dfinity and beyond.</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[The State Of Progressive Web Apps Adoption By Developers In 2021]]></title>
          <description><![CDATA[Are PWA preached or adopted by developers? Do they use these on mobile and desktop? Do they develop these even if their support on iOS is not optimal? Do they think Apple will ever implement Web Push Notifications?]]></description>
          <link>https://daviddalbusco.com/blog/the-state-of-progressive-web-apps-adoption-by-developers-in-2021/</link>
          <pubDate>Tue, 22 Jun 2021 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*xFkV_aTp7qE6kKOt1NxCbA.jpeg" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@monicasauro?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Monica Sauro</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<hr />
<p>For the second year in a row, I ran some polls on <a href="https://twitter.com/daviddalbusco" rel="noopener noreferrer external">Twitter</a> about the relationship between developers and Progressive Web Apps.</p>
<p>Are PWA preached or adopted by developers? Do they use these on mobile and desktop? Do they develop these even if their support on iOS is not optimal? Do they think Apple will ever implement Web Push Notifications?</p>
<p>Here are some interesting facts and figures resulting of the new edition of my surveys.</p>
<hr />
<h3>Limitation</h3>
<p>I am not a statistician nor an expert in the redaction and interpretation of polls. In addition, the surveys happened on Twitter, therefore the answers were given by the people within my reach.</p>
<p>For such reasons, please do interpret following figures and comments as information, not facts.</p>
<p>Note also I am an Open Source and Progressive Web Apps aficionado. I will try my best to remain impartial in my writing 😉.</p>
<hr />
<h3>58% Of The Developers Do Not Use Installed PWA On Mobile</h3>
<p>I am using on a daily basis the Progressive Web Apps of <a href="https://dev.to/" rel="noopener noreferrer external">DEV.to</a> and <a href="https://twitter.com" rel="noopener noreferrer external">Twitter</a>. That’s why I was firstly interested in the question of adoption regarding the usage on mobile devices.</p>
<p>To my surprise, <strong>58%</strong> of the developers are not using any PWA, installed on their phone home screen, on a weekly basis. That’s <strong>–11%</strong> fewer developers using regularly such applications instead of native apps (those you get through the App Store or Google Play Store) than previous survey.</p>
<p><a href="https://twitter.com/daviddalbusco/status/1404833211566505989?s=20" rel="noopener noreferrer external"><img src="https://daviddalbusco.com/assets/images/1*jrIhvU2vD9duQ08Kgl4GNg.png" alt="" / loading="lazy"></a></p>
<p>Of course both types of applications are not mutually exclusive. It is common to say that PWA are a good fit to acquire early adopters and, that native fits well loyal customers.</p>
<p>The question also emphases the installation of the application on the devices’ home screen. If we ignore this variable and, would also consider the apps that are used directly in the browser, I would tend to think that the number of users would be probably higher.</p>
<p>That being said, roughly summarized, half of the developers do not use Progressive Web Apps installed on their mobile devices on a weekly basis.</p>
<hr />
<h3>58% Of Those Who Do Not Use PWA Are On iOS</h3>
<p>The Progressive Web Apps is not optimal on iOS, I can notably mention the lack of installation prompt and, the absence of Web Push Notifications, I was looking to discover if, as previous year, the adoption rate was link to the operating system.</p>
<p>In the end, the answer is not so clear. <strong>58%</strong> of those who do not use PWA have iPhones.</p>
<p><a href="https://twitter.com/daviddalbusco/status/1406271148904615938?s=20" rel="noopener noreferrer external"><img src="https://daviddalbusco.com/assets/images/1*2lclIWf7Rcy1f14xfRMMSg.png" alt="" / loading="lazy"></a></p>
<p>Such number is quite stable (-5%) in comparison to previous survey and still display a bigger “rejection” rate on iOS. Nevertheless, the gap is not that big.</p>
<p>However, among all results of this blog post, this particular one is to my opinion the one that should be taken with the most hindsight. I ran its poll on a Saturday and, it gathered really few answers.</p>
<hr />
<h3>30% On Desktop</h3>
<p>After-all, what if in addition or instead of mobile devices, the future of PWA would not be primarily on desktop? This is one of the hypotheses which followed last year survey and, which I was willing to test.</p>
<p>As a result, I discovered that <strong>30%</strong> of the developers are using PWA installed on their computers (pinned to dock, desktop or start menu) on a weekly basis.</p>
<p><a href="https://twitter.com/daviddalbusco/status/1405211451531354128?s=20" rel="noopener noreferrer external"><img src="https://daviddalbusco.com/assets/images/1*OAg7Gs-VzLovW0MgXWyFTQ.png" alt="" / loading="lazy"></a></p>
<p>On this type of device we are used to installing applications or using browser’s favorites features, that’s why I particularly find this number quite interesting.</p>
<p>Furthermore, being able to install Progressive Web Apps on the desktop, with the help of an installation button in the browsers, is something which can be still considered as, to some extension, a recent feature (if I am not wrong, for example introduced two years ago in Chrome v76 for the first time).</p>
<hr />
<h3>72% Developed New PWAs in 2020</h3>
<p>Adoption as a user is something but, what about the development behavior. Do the developers developed one or many Progressive Web Apps in 2020?</p>
<p>As a matter of fact, they did. <strong>72%</strong> of the devs have initialized new PWAs last year.</p>
<p><a href="https://twitter.com/daviddalbusco/status/1405569508224520196?s=20" rel="noopener noreferrer external"><img src="https://daviddalbusco.com/assets/images/1*nVLCghVeYDIyALWhfI73JQ.png" alt="" / loading="lazy"></a></p>
<p>This question would probably need some more questions to refine the granularity of the question (do they “just” added a worker to cache the assets or did they develop the applications with offline first strategies? etc.) but, for the majority it seems that it is the way to go development wise.</p>
<hr />
<h3>One Third Have Published PWA In Google Play Store</h3>
<p>Thanks to the help of <a href="https://github.com/GoogleChromeLabs/bubblewrap" rel="noopener noreferrer external">bubblewrap</a> and tools build around it as the <a href="https://www.pwabuilder.com/" rel="noopener noreferrer external">PWABuilder</a>, it is possible relatively easily to publish Progressive Web Apps in the Google Play Store.</p>
<p>Because I am quite a fan of this solution, I wondered if other developers were too.</p>
<p>According to my surveys, <strong>18</strong> developers within my reach (one third of those who answered the poll) have already published their Progressive Web Apps in the Google Play Store.</p>
<p><a href="https://twitter.com/daviddalbusco/status/1405926594716352521?s=20" rel="noopener noreferrer external"><img src="https://daviddalbusco.com/assets/images/1*SOhysyb0r8PvoPGbL63Qfw.png" alt="" / loading="lazy"></a></p>
<p><a href="https://developer.chrome.com/docs/android/trusted-web-activity/overview/" rel="noopener noreferrer external">Trusted Web Activities</a>, the new option defined by Google to open web-app such as PWA on Android is a fairly new solution. It was introduced, if I am not wrong, begin of year2020. Therefore, it is an interesting result to get to know that some early developer’s adopters have already used such medium.</p>
<hr />
<h3>Will Apple Implement Web Push Notifications?</h3>
<p>To end up the surveys with a question, which to my eyes has a direct link with the adoption rate of Progressive Web Apps on mobile devices, I asked developers if they think Apple will ever support Web Push Notifications on iOS.</p>
<p>After-all, they do already support such feature on OSX but, do not on iOS devices (even though their strategy seems to merge both experience, think as for example about the M1 processor 🤷‍)️.</p>
<p><strong>15%</strong> think it will happen for sure, and for the rest, the developers are divided between whether it can happen or never (both <strong>42.5%</strong>).</p>
<p><a href="https://twitter.com/daviddalbusco/status/1406667470363152387?s=20" rel="noopener noreferrer external"><img src="https://daviddalbusco.com/assets/images/1*i4k-eO0d0TqPTvc9ArBi-A.png" alt="" / loading="lazy"></a></p>
<p>Not really a surprise to me that the trust in Apple is low on Twitter when it comes to the topic Web and Progressive Web Apps. Interesting on the other hand is that it is not a clear “all or nothing” scenario. Many developers do think it might happen. I would guess that probably some, as I do, think states’ regulations or anti-trust might be able someday to force Apple to open its mobile system. Remember, even if you install Chrome or Firefox on your iPhone, it is still Safari underneath.</p>
<hr />
<h3>Conclusion</h3>
<p>I have got a bit mixed feeling about the results of this survey. To some extension, as a Progressive Web Apps aficionado, I am a bit disappointed that fewer developers seem to use these on a weekly basis on mobile devices. On the contrary, I am happy to notice the promising adoption’s rate on desktop and, that others ship their PWA to the Google Play Store too.</p>
<p>I just added a reminder for June 2022 in my calendar to run such surveys again and, I am already looking forward to the results.</p>
<p>To infinity and beyond!</p>
<p>David</p>
<p>P.S.: You can read the results of the previous surveys, those of 2020, <a href="https://daviddalbusco.com/blog/the-state-of-progressive-web-apps-adoption-by-developers" rel="noopener noreferrer external">here</a>.</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[CSS nth- Selectors Variable]]></title>
          <description><![CDATA[A practical hack to make CSS nth- selectors variable within Web Components.]]></description>
          <link>https://daviddalbusco.com/blog/css-nth-selectors-variable/</link>
          <pubDate>Wed, 09 Jun 2021 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*bCYvMc19HUEZEmZylGOoGg.jpeg" alt="" / loading="lazy"><br />
<em>Photo by <a href="https://unsplash.com/@mariogogh?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Mario Gogh</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>Using CSS variables, at least when I’m writing these lines in June 2021, is not supported in media queries or selector, e.g. <code>:nth-child(var(--my-variable))</code> does not work.</p>
<p>This is a bit unfortunate but, not unsolvable. In some recent development I bypassed this limitation by injecting <code>style</code> elements in the DOM, into my Web Components, in order to animate block of codes in <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>.</p>
<iframe width="280" height="158" src="https://www.youtube.com/embed/nast1X6dKo8" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
<br/>
<hr />
<h3>Introduction</h3>
<p>Stricto sensu the following trick is not reserve to Web Components and, probably works with any elements too. I just only used it so far with such technology 😜.</p>
<p>I will first display the idea with the help a vanilla component and, end the article with the same approach but, implemented with a <a href="https://stenciljs.com/" rel="noopener noreferrer external">StencilJS</a> functional component.</p>
<hr />
<h3>Goal Of The Tutorial</h3>
<p>We are going to develop a Web Component which renders a <code>&lt;ul/&gt;</code> list and, which can animate the display of its entries.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*V_biZYrvRQsoYUdph7U_qQ.gif" alt="" / loading="lazy"></p>
<p>No semantic elements are going to be added or removed from the DOM once the component is load. The animation will happen by modifying the <code>style</code> , more precisely by applying a different style on the selected <code>li:nth-child(n)</code> .</p>
<hr />
<h3>Vanilla JS</h3>
<p>To display the idea without anything else than the web, we create an <code>index.html</code> page. It consumes the Vanilla component we are about to develop. We also add a <code>button</code> to triggers the animation.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">html</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">head</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#50FA7B;font-style:italic"> type</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">module</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">./my-component.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">head</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">body</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">my-component</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">my-component</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">button</span><span style="color:#F8F8F2">>Next&#x3C;/</span><span style="color:#FF79C6">button</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            document</span></span>
<span class="line"><span style="color:#F8F8F2">                .</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">button</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">                .</span><span style="color:#50FA7B">addEventListener</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">click</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> document.</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">my-component</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">).</span><span style="color:#50FA7B">next</span><span style="color:#F8F8F2">());</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">body</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">html</span><span style="color:#F8F8F2">></span></span></code></pre><p>In a separate file, called <code>my-component.js</code> , we create the Web Component. At this point without any animation. We declare it <a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM" rel="noopener noreferrer external">open</a> to be able to access the shadow DOM (through <code>shadowRoot</code> ), we create a style to hide all <code>li</code> and define the <code>transition</code> . Finally, we add the <code>ul</code> list and its children <code>li</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">class</span><span style="color:#8BE9FD"> MyComponent</span><span style="color:#FF79C6"> extends</span><span style="color:#8BE9FD;font-style:italic"> HTMLElement</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    constructor</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        super</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">attachShadow</span><span style="color:#F8F8F2">({ mode</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">open</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> style </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">initStyle</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> ul </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">initElement</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        this</span><span style="color:#F8F8F2">.shadowRoot.</span><span style="color:#50FA7B">appendChild</span><span style="color:#F8F8F2">(style);</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        this</span><span style="color:#F8F8F2">.shadowRoot.</span><span style="color:#50FA7B">appendChild</span><span style="color:#F8F8F2">(ul);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    connectedCallback</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        this</span><span style="color:#F8F8F2">.className </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">hydrated</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    next</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#6272A4">        // TODO in next chapter</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    initStyle</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> style </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> document.</span><span style="color:#50FA7B">createElement</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">style</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        style.innerHTML </span><span style="color:#FF79C6">=</span><span style="color:#F1FA8C"> `</span></span>
<span class="line"><span style="color:#F1FA8C">          :host {</span></span>
<span class="line"><span style="color:#F1FA8C">            display: block;</span></span>
<span class="line"><span style="color:#F1FA8C">          }</span></span>
<span class="line"><span style="color:#F1FA8C">          </span></span>
<span class="line"><span style="color:#F1FA8C">          li {</span></span>
<span class="line"><span style="color:#F1FA8C">            opacity: 0;</span></span>
<span class="line"><span style="color:#F1FA8C">            transition: opacity 0.5s ease-out;</span></span>
<span class="line"><span style="color:#F1FA8C">          }</span></span>
<span class="line"><span style="color:#F1FA8C">        `</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> style;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    initElement</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> ul </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> document.</span><span style="color:#50FA7B">createElement</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ul</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> li1 </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> document.</span><span style="color:#50FA7B">createElement</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">li</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">        li1.innerHTML </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Spine</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> li2 </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> document.</span><span style="color:#50FA7B">createElement</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">li</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">        li2.innerHTML </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Cowboy</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> li3 </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> document.</span><span style="color:#50FA7B">createElement</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">li</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">        li3.innerHTML </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Shelving</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        ul.</span><span style="color:#50FA7B">append</span><span style="color:#F8F8F2">(li1, li2, li3);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> ul;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">customElements.</span><span style="color:#50FA7B">define</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">my-component</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, MyComponent);</span></span></code></pre><p>At this point, if we open our example in a browser (<code>npx serve .</code>), we should find a component, with a hidden content, and a button which has no effect yet. Not much to see, but that’s a start 😁.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*ODDEgNnoHmsbGwgIPuT5zA.png" alt="" / loading="lazy"></p>
<p>To develop the animation, we have to keep track of the displayed <code>li</code> , that’s why we add a state (<code>index</code>) to the component.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">class</span><span style="color:#8BE9FD"> MyComponent</span><span style="color:#FF79C6"> extends</span><span style="color:#8BE9FD;font-style:italic"> HTMLElement</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    index </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    constructor</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">...</span></span></code></pre><p>Thanks to it, we can implement the <code>next()</code> method, the one called from the button we added earlier in the HTML page.</p>
<blockquote>
<p>Not my most beautiful code ever. Let’s agree it has only a demo purpose 😅.</p>
</blockquote>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">next</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.index </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.index </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> 3</span><span style="color:#FF79C6"> ?</span><span style="color:#BD93F9"> 1</span><span style="color:#FF79C6"> :</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.index </span><span style="color:#FF79C6">+</span><span style="color:#BD93F9"> 1</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> selector </span><span style="color:#FF79C6">=</span><span style="color:#F1FA8C"> `</span></span>
<span class="line"><span style="color:#F1FA8C">      li:nth-child(</span><span style="color:#FF79C6">${</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.index</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">) {</span></span>
<span class="line"><span style="color:#F1FA8C">        opacity: 1;</span></span>
<span class="line"><span style="color:#F1FA8C">      }</span></span>
<span class="line"><span style="color:#F1FA8C">    `</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> style </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.shadowRoot.</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">style#animation</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (style) {</span></span>
<span class="line"><span style="color:#F8F8F2">        style.innerHTML </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> selector;</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    style </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> document.</span><span style="color:#50FA7B">createElement</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">style</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    style.</span><span style="color:#50FA7B">setAttribute</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">id</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">animation</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    style.innerHTML </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> selector;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.shadowRoot.</span><span style="color:#50FA7B">appendChild</span><span style="color:#F8F8F2">(style);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>What’s happening there?</p>
<p>It first set the next <code>index</code> , <code>li</code>, to be displayed and, create a CSS <code>selector</code> to apply the <code>opacity</code> styling. In short, this replaces the CSS variable we cannot use.</p>
<p>Afterwards, we check if the shadowed content of our Web Component already contains a dedicated style to apply the animation. If it does, we update the style with the new value — selector and, if not, we create a new style tag.</p>
<p>Each time this method is call, a new <code>style</code> is applied and, therefore another <code>li:nth-child(n)</code> is displayed.</p>
<p>If we open again our browser to give it a try, items should be animated among click on our button <code>next</code> and, if we go further and observe the component in the inspector, we should notice that the shadowed <code>style</code> element change on each method call.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*G76KWZf9do9aBnCo2rtLdw.png" alt="" / loading="lazy"></p>
<hr />
<h3>StencilJS</h3>
<p>Let’s double the fun with the same example but, using a <a href="https://stenciljs.com/" rel="noopener noreferrer external">StencilJS</a> functional component 🤙.</p>
<blockquote>
<p>You can start a new project with the command line <code>npm init stencil</code></p>
</blockquote>
<p>Because we are developing the exact same component, we can copy the previous HTML content (declaring the component and, adding a <code>button</code> ) in the <code>./src/index.html</code> of the project with an only slight small difference, the method <code>next()</code> has to be declared and, called with async — await. This is a requirement — best practice of Stencil, public method of components have to be <code>async</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;!</span><span style="color:#FF79C6">doctype</span><span style="color:#50FA7B;font-style:italic"> html</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">html</span><span style="color:#50FA7B;font-style:italic"> dir</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ltr</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">en</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">head</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">meta</span><span style="color:#50FA7B;font-style:italic"> charset</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">utf-8</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">meta</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            name</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">viewport</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            content</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=5.0</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">        /></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">title</span><span style="color:#F8F8F2">>Stencil Component Starter&#x3C;/</span><span style="color:#FF79C6">title</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#50FA7B;font-style:italic"> type</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">module</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/build/demo-stencil.esm.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#50FA7B;font-style:italic"> nomodule</span><span style="color:#50FA7B;font-style:italic"> src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/build/demo-stencil.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">head</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">body</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#6272A4">        &#x3C;!-- Same code as in previous chapter --></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">my-component</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">my-component</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">button</span><span style="color:#F8F8F2">>Next&#x3C;/</span><span style="color:#FF79C6">button</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            document</span></span>
<span class="line"><span style="color:#F8F8F2">                .</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">button</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">                .</span><span style="color:#50FA7B">addEventListener</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">click</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> document.</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">my-component</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">).</span><span style="color:#50FA7B">next</span><span style="color:#F8F8F2">());</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#6272A4">        &#x3C;!-- Same code as in previous chapter --></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">body</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">html</span><span style="color:#F8F8F2">></span></span></code></pre><p>We can also repeat previous steps and, create first a component which does nothing else than rendering a <code>ul</code> list and, hidden items <code>li</code>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Component, h } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@stencil/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    tag</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">my-component</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    styles</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> `</span></span>
<span class="line"><span style="color:#F1FA8C">        :host {</span></span>
<span class="line"><span style="color:#F1FA8C">            display: block;</span></span>
<span class="line"><span style="color:#F1FA8C">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F1FA8C">        li {</span></span>
<span class="line"><span style="color:#F1FA8C">            opacity: 0;</span></span>
<span class="line"><span style="color:#F1FA8C">            transition: opacity 0.5s ease-out;</span></span>
<span class="line"><span style="color:#F1FA8C">        }</span></span>
<span class="line"><span style="color:#F1FA8C">    `</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    shadow</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> MyComponent</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">ul</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF79C6">li</span><span style="color:#F8F8F2">>Spine&#x3C;/</span><span style="color:#FF79C6">li</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF79C6">li</span><span style="color:#F8F8F2">>Cowboy&#x3C;/</span><span style="color:#FF79C6">li</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF79C6">li</span><span style="color:#F8F8F2">>Shelving&#x3C;/</span><span style="color:#FF79C6">li</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#FF79C6">ul</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        );</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>By testing the component (<code>npm run start</code>) we should get the same result too 😉.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*qw2VVn0rZlENtjlYY9Yp1Q.png" alt="" / loading="lazy"></p>
<p>To keep track of the <code>li</code> to highlight, we need a state and, the function <code>state</code> . We add both to our component.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">State</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">private index: number </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Method</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">async </span><span style="color:#50FA7B">next</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">  this</span><span style="color:#F8F8F2">.index </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.index </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> 3</span><span style="color:#FF79C6"> ?</span><span style="color:#BD93F9"> 1</span><span style="color:#FF79C6"> :</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.index </span><span style="color:#FF79C6">+</span><span style="color:#BD93F9"> 1</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>In comparison to the Vanilla component, because we are using a bundler which simplify the development, we do not have to take care of re-rendering by our selves. Each modification of the <code>state</code> will trigger a re-render which, ultimately, update the nodes which have to be updated (and only those who have to be updated).</p>
<p>Still, we do have to implement the CSS selector variable. For such purpose, as briefly mentioned, we are going to use a functional component. It might work with a class component but, I feel like a functional one is well suited for the job.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> Animate</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> FunctionalComponent</span><span style="color:#F8F8F2">&#x3C;{index</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#F8F8F2">;}> </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> ({</span><span style="color:#FFB86C;font-style:italic">index</span><span style="color:#F8F8F2">}) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">style</span><span style="color:#F8F8F2">></span><span style="color:#FF79C6">{</span><span style="color:#F1FA8C">`</span></span>
<span class="line"><span style="color:#F1FA8C">    li:nth-child(</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">index</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">) {</span></span>
<span class="line"><span style="color:#F1FA8C">      opacity: 1;</span></span>
<span class="line"><span style="color:#F1FA8C">    }</span></span>
<span class="line"><span style="color:#F1FA8C">  `</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">style</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  );</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>This component renders a <code>style</code> element for the value we path as parameter, our <code>state</code>.</p>
<p>Finally, we have to use the functional component and, bind it to our state value. Doing so, it will be re-rendered each time its value changes.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Host</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Animate</span><span style="color:#50FA7B;font-style:italic"> index</span><span style="color:#FF79C6">={</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.index</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">Animate</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">ul</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#FF79C6">li</span><span style="color:#F8F8F2">>Spine&#x3C;/</span><span style="color:#FF79C6">li</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#FF79C6">li</span><span style="color:#F8F8F2">>Cowboy&#x3C;/</span><span style="color:#FF79C6">li</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#FF79C6">li</span><span style="color:#F8F8F2">>Shelving&#x3C;/</span><span style="color:#FF79C6">li</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">ul</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">Host</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>That’s already it, we were able to replicate the same component 🥳.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*9DoGPKFedgsKnVMQp5wWIg.gif" alt="" / loading="lazy"></p>
<p>The above component in a single block of code:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Component, FunctionalComponent, h, Host, Method, State } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@stencil/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> Animate</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> FunctionalComponent</span><span style="color:#F8F8F2">&#x3C;{index</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#F8F8F2">;}> </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> ({</span><span style="color:#FFB86C;font-style:italic">index</span><span style="color:#F8F8F2">}) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">style</span><span style="color:#F8F8F2">></span><span style="color:#FF79C6">{</span><span style="color:#F1FA8C">`</span></span>
<span class="line"><span style="color:#F1FA8C">    li:nth-child(</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">index</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">) {</span></span>
<span class="line"><span style="color:#F1FA8C">      opacity: 1;</span></span>
<span class="line"><span style="color:#F1FA8C">    }</span></span>
<span class="line"><span style="color:#F1FA8C">  `</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">style</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  );</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  tag</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">my-component</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  styles</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> `:host {</span></span>
<span class="line"><span style="color:#F1FA8C">      display: block;</span></span>
<span class="line"><span style="color:#F1FA8C">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F1FA8C">    li {</span></span>
<span class="line"><span style="color:#F1FA8C">      opacity: 0;</span></span>
<span class="line"><span style="color:#F1FA8C">      transition: opacity 0.5s ease-out;</span></span>
<span class="line"><span style="color:#F1FA8C">    }</span></span>
<span class="line"><span style="color:#F1FA8C">  `</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  shadow</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> MyComponent</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  @</span><span style="color:#50FA7B">State</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#F8F8F2"> index</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#FF79C6"> =</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  @</span><span style="color:#50FA7B">Method</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#FF79C6">  async</span><span style="color:#50FA7B"> next</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.index </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.index </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> 3</span><span style="color:#FF79C6"> ?</span><span style="color:#BD93F9"> 1</span><span style="color:#FF79C6"> :</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.index </span><span style="color:#FF79C6">+</span><span style="color:#BD93F9"> 1</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Host</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Animate</span><span style="color:#50FA7B;font-style:italic"> index</span><span style="color:#FF79C6">={</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.index</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">Animate</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#FF79C6">ul</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">li</span><span style="color:#F8F8F2">>Spine&#x3C;/</span><span style="color:#FF79C6">li</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">li</span><span style="color:#F8F8F2">>Cowboy&#x3C;/</span><span style="color:#FF79C6">li</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">li</span><span style="color:#F8F8F2">>Shelving&#x3C;/</span><span style="color:#FF79C6">li</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;/</span><span style="color:#FF79C6">ul</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">Host</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>Summary</h3>
<p>I am honestly not sure that this article will ever find its audience, nor do I think it may be useful to someone some day but, well, I love to use that trick 😜. In addition, it was fun to develop for demo purpose the same piece of code with Vanilla JS or Stencil.</p>
<p>To infinity and beyond!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Build A Library With esbuild]]></title>
          <description><![CDATA[How to bundle ESM, IIFE or CommonJS libraries with esbuild.]]></description>
          <link>https://daviddalbusco.com/blog/build-a-library-with-esbuild/</link>
          <pubDate>Mon, 24 May 2021 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*6sA4hPVV9wO_jo8l205Aig.jpeg" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@imagefactory?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Colin Watts</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I recently developed plugins and, migrated all the utilities of <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a> to build these with <a href="https://esbuild.github.io/" rel="noopener noreferrer external">esbuild</a>.</p>
<p>If you are looking to do the same, hope this tutorial helps you get started!</p>
<hr />
<h3>Introduction</h3>
<p><a href="https://esbuild.github.io/" rel="noopener noreferrer external">esbuild</a> is “an extremely fast JavaScript bundler” made by <a href="https://twitter.com/evanwallace" rel="noopener noreferrer external">Evan Wallace</a>. It is its tagline and, according my tests, the least we can say is that it is true. It is blazing fast ⚡️.</p>
<p>Sometimes while migrating my libraries, I even found myself waiting for the end of a build because I did not notice that it was already finished. I assumed it would still need some more time, old habits die hard I guess 😅.</p>
<p>In addition, other things which make me really like this new bundler are its clean, flexible API and, its documentation. It is easy to follow and, clear.</p>
<hr />
<h3>Setup</h3>
<p>To get started, let’s create a new empty project.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">mkdir</span><span style="color:#F1FA8C"> mylib</span><span style="color:#F8F8F2"> &#x26;&#x26; </span><span style="color:#8BE9FD">cd</span><span style="color:#F1FA8C"> mylib</span><span style="color:#F8F8F2"> &#x26;&#x26; </span><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> init</span><span style="color:#BD93F9"> --yes</span></span></code></pre><p>You can use esbuild to bundle libraries from vanilla JavaScript source files but, at least in this tutorial, we are going to use TypeScript too. That’s why, in addition to the bundler, we also install it and <a href="https://github.com/isaacs/rimraf" rel="noopener noreferrer external">rimraf</a>, to remove the output folder before any new build.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> i</span><span style="color:#F1FA8C"> esbuild</span><span style="color:#F1FA8C"> typescript</span><span style="color:#F1FA8C"> rimraf</span><span style="color:#BD93F9"> --save-dev</span></span></code></pre><p>At the root of our project, we create a <code>./tsconfig.json</code> file to indicates that the directory is the root of a TypeScript project.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">compilerOptions</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">declaration</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">target</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">esnext</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">lib</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">esnext</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">dom</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">strict</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">noImplicitAny</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">esModuleInterop</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">moduleResolution</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">node</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">outDir</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">lib</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>In the above configuration, I set <code>esnext</code> to target the most recent ES standard and, also set <code>declaration</code> to <code>true</code> . esbuild not generating the declarations, we are going to generate these using the <code>tsc</code> command.</p>
<p>We update our <code>./package.json</code> with a script to <code>build</code> our library and, we define a new <code>types</code> entry which should point to the types declarations.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">name</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">mylib</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">version</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">1.0.0</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">description</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> ""</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">main</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">lib/index.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">types</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">lib/index.d.ts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">scripts</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">ts-types</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C"> tsc --emitDeclarationOnly --outDir lib</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">build</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">rimraf lib &#x26;&#x26; node ./esbuild.js &#x26;&#x26; npm run ts-types</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    },</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">keywords</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [],</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">author</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> ""</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">license</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">ISC</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">devDependencies</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">esbuild</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">^0.12.1</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">typescript</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">^4.2.4</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>The configuration and, build using esbuild is going to be developed in following chapters in a dedicated file, <code>./esbuild.js</code> . That’s why, at this point, we leave it empty.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">touch</span><span style="color:#F1FA8C"> esbuild.js</span></span></code></pre><p>It is worth to notice that the esbuild commands can be inlined inside the <code>scripts</code> tag of the <code>package.json</code> but, I personally like to handle it separately.</p>
<p>That’s useful when the scripts evolve or, when multiple miscellaneous builds and, steps are performed.</p>
<p>Finally, we add some source code to be compiled, such as following sample function, in a main new entry point file <code>./src/index.ts</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> add</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">a</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">b</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#FF79C6"> =></span><span style="color:#F8F8F2"> a </span><span style="color:#FF79C6">+</span><span style="color:#F8F8F2"> b;</span></span></code></pre><hr />
<h3>ECMAScript module</h3>
<p>The <code>esm</code> format stands for &quot;ECMAScript module&quot;. It assumes the environment supports <code>import</code> and <code>export</code> syntax (<a href="https://esbuild.github.io/api/#format-esm" rel="noopener noreferrer external">documentation</a>).</p>
<p>To bundle such a modern library, we can add the following configuration to our <code>./esbuild.js</code> :</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> esbuild </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> require</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">esbuild</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">esbuild</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">build</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        entryPoints</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">src/index.ts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">        outdir</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">lib</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        bundle</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        sourcemap</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        minify</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        splitting</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        format</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">esm</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        target</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">esnext</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">]</span></span>
<span class="line"><span style="color:#F8F8F2">    })</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">catch</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> process.</span><span style="color:#50FA7B">exit</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">));</span></span></code></pre><p>That’s already it 🥳.</p>
<p>If we run <code>npm run build</code> in our project, the library is bundled to <code>esm</code> , a source map is generated and, the TypeScript declarations as well.</p>
<p>“But, what’s happening here” you may ask yourself? Therefore, here some context:</p>
<p>In the script we first require <code>esbuild</code> and, with the help of the method <code>.build</code> , we run an <code>async</code> compilation. It is also possible to perform this step synchronously and, get the results (I will develop this in a following chapter).</p>
<p>To perform the operation, we set following options:</p>
<ul>
<li><code>entryPoints</code> and <code>ourdir</code> defines which files need to be bundled to which output</li>
<li><code>bundle</code> means to inline any imported dependencies into the file itself. This process is recursive so dependencies of dependencies (and so on) will also be inlined (<a href="https://esbuild.github.io/api/#bundle" rel="noopener noreferrer external">documentation</a>). In other words, if you have got <code>import</code> in your <code>entryPoints</code> , bundle will resolve these to add their code in the results.</li>
<li><code>sourcemap</code> if set to <code>true</code> , generates source map files next to your JavaScript outcome</li>
<li><code>minify</code> makes the code smaller (<a href="https://esbuild.github.io/api/#minify" rel="noopener noreferrer external">documentation</a>)</li>
<li><code>splitting</code> is a work in progress (at the time I write these lines) which improves the code sharing between multiple endpoints (see <a href="https://esbuild.github.io/api/#splitting" rel="noopener noreferrer external">documentation</a>)</li>
<li><code>format</code> is set to <code>esm</code> as it is the goal in this chapter (<a href="https://esbuild.github.io/api/#format-commonjs" rel="noopener noreferrer external">documentation</a>)</li>
<li><code>target</code> defines which types of JavaScript we want to output. In our case, only the most recent version (<a href="https://esbuild.github.io/api/#target" rel="noopener noreferrer external">documentation</a>)</li>
</ul>
<hr />
<h4>Module Field</h4>
<p>In above chapter we are generating an <code>esm</code> library. If you aim to use this tutorial to create an effective bundle, I suggest adding a <code>module</code> entry in our <code>package.json</code> . Even though not officially documented, this entry is useful for such types of library (see <a href="https://stackoverflow.com/questions/42708484/what-is-the-module-package-json-field-for" rel="noopener noreferrer external">Stackoverflow</a>).</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#E9F284">"</span><span style="color:#F1FA8C">main</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">: </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">lib/index.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">"</span><span style="color:#F1FA8C">module</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">: </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">lib/index.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">"</span><span style="color:#F1FA8C">types</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">: </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">lib/index.d.ts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span></code></pre><hr />
<h3>IIFE</h3>
<p>The <code>iife</code> format stands for &quot;immediately-invoked function expression&quot; and is intended to be run in the browser (<a href="https://esbuild.github.io/api/#format-iife" rel="noopener noreferrer external">documentation</a>).</p>
<p>If you rather like or, are in need to create library which is immediately available, it can be done by removing from the previous configuration both <code>format</code> and <code>splitting</code> .</p>
<p>The <code>iife</code> format is the default format unless we set <code>platform</code> to node (as in next chapter). <code>splitting</code> is only available for <code>esm</code> modules.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> esbuild </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> require</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">esbuild</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">esbuild</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">build</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        entryPoints</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">src/index.ts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">        outdir</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">lib</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        bundle</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        sourcemap</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        minify</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        target</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">esnext</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">]</span></span>
<span class="line"><span style="color:#F8F8F2">    })</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">catch</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> process.</span><span style="color:#50FA7B">exit</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">));</span></span></code></pre><hr />
<h3>CommonJS — Node</h3>
<p>The <code>cjs</code> format stands for &quot;CommonJS&quot; and is intended to be run in node (<a href="https://esbuild.github.io/api/#format-commonjs" rel="noopener noreferrer external">documentation</a>).</p>
<p>If your library aims to be used in a Node or, in a non-browser environment, it can be bundled for such purpose with a related <code>platform</code> option.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> esbuild </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> require</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">esbuild</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">esbuild</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">build</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        entryPoints</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">src/index.ts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">        outdir</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">lib</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        bundle</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        sourcemap</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        minify</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        platform</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">node</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        target</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">node10.4</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">]</span></span>
<span class="line"><span style="color:#F8F8F2">    })</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">catch</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> process.</span><span style="color:#50FA7B">exit</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">));</span></span></code></pre><p>In this configuration we define <code>node</code> as <code>platform</code> and, set <code>target</code> to Node version 10 (<a href="https://esbuild.github.io/api/#target" rel="noopener noreferrer external">documentation</a>).</p>
<hr />
<h3>Synchronous Build</h3>
<p>Above builds are asynchronous. You might want to run a synchronous builds to either get information on the process or, do something with the output without writing it to the filesystem.</p>
<p>This can be achieved by replacing the method <code>.build</code> with <code>.buildSync</code> .</p>
<hr />
<h4>Information</h4>
<p>To get to know if there were errors, or warnings, we can call <code>buildSync</code> . It will return an object which contains such information.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> esbuild </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> require</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">esbuild</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> results </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> esbuild.</span><span style="color:#50FA7B">buildSync</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    entryPoints</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">src/index.ts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">    outdir</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">lib</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    bundle</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    sourcemap</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    minify</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    splitting</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    format</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">esm</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    target</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">esnext</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">]</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(results);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// Output:</span></span>
<span class="line"><span style="color:#6272A4">// { errors: [], warnings: [] }</span></span></code></pre><hr />
<h4>In Memory Results</h4>
<p>To get the files that would have been written as in-memory buffers, we can leverage the option <code>write</code>(<a href="https://esbuild.github.io/api/#write" rel="noopener noreferrer external">documentation</a>). For each <code>entryPoints</code>, esbuild will answer with a related <code>outputFiles</code> entry in an array of results.</p>
<p>For example, if we would like to inline our script in an HTML file, we would be able to get these results and, parse it manually to the output of our choice.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> esbuild </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> require</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">esbuild</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> { readFile, writeFile, mkdir } </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> require</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">fs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">).promises;</span></span>
<span class="line"><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#50FA7B"> mkdir</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">./lib</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> script </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> esbuild.</span><span style="color:#50FA7B">buildSync</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        entryPoints</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">src/index.ts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">        bundle</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        minify</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        format</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">esm</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        target</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">esnext</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">        write</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> false</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> html </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> readFile</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">src/index.html</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">utf8</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#50FA7B"> writeFile</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#E9F284">        "</span><span style="color:#F1FA8C">lib/index.html</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F1FA8C">        `&#x3C;script type="module"></span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">script.outputFiles[</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">].text</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">&#x3C;/script></span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">html</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">})();</span></span></code></pre><p>A bit out of the scope of this article but, to demonstrate how flexible esbuild is, we would be able to install a html minifier to get even more fancier.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> i</span><span style="color:#F1FA8C"> html-minifier-terser</span><span style="color:#BD93F9"> --save-dev</span></span></code></pre><p>Once added to our project, we would be able to minify the resulting HTML and, are almost already ready to turn this small library in a build pipeline for modern application 😱.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> esbuild </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> require</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">esbuild</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> {readFile, writeFile, mkdir} </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> require</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">fs</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">).promises;</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> minify </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> require</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">html-minifier-terser</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">).minify;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#50FA7B"> mkdir</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">./lib</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> script </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> esbuild</span></span>
<span class="line"><span style="color:#F8F8F2">        .</span><span style="color:#50FA7B">buildSync</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">            entryPoints</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">src/index.ts</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">            bundle</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">            minify</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">            format</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">esm</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">            target</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">esnext</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">            write</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> false</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> html </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> readFile</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">src/index.html</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">utf8</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);¨</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> minifyOptions </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        collapseWhitespace</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        keepClosingSlash</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        removeComments</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        removeRedundantAttributes</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        removeScriptTypeAttributes</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        removeStyleLinkTypeAttributes</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        useShortDoctype</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        minifyCSS</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#50FA7B"> writeFile</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#E9F284">        '</span><span style="color:#F1FA8C">lib/index.html</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F1FA8C">        `&#x3C;script></span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">script.outputFiles[</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">].text</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">&#x3C;/script></span><span style="color:#FF79C6">${</span><span style="color:#FF79C6">await</span><span style="color:#50FA7B"> minify</span><span style="color:#F8F8F2">(html</span><span style="color:#F1FA8C">, </span><span style="color:#F8F8F2">minifyOptions)</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">})();</span></span></code></pre><hr />
<h3>Summary</h3>
<p><a href="https://esbuild.github.io/" rel="noopener noreferrer external">esbuild</a> is slick 🤙.</p>
<p>To infinity and beyond!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[The MutationObserver Web API]]></title>
          <description><![CDATA[An introduction to the MutationObserver Web API which provides the ability to watch for changes being made to the document.]]></description>
          <link>https://daviddalbusco.com/blog/the-mutationobserver-web-api/</link>
          <pubDate>Tue, 11 May 2021 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*uslRrEbuAeXmZvnKzhpPaQ.jpeg" alt="" / loading="lazy"></p>
<p><em>Source forum <a href="https://www.resetera.com/threads/magneto-or-professor-x-who-do-you-side-with-more.291911/" rel="noopener noreferrer external">resetera</a></em></p>
<p>I recently developed multiple features across projects with the help of the <a href="https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver" rel="noopener noreferrer external">MutationObserver Web API</a>. A bit to my surprise, I noticed that some colleagues never had used it or, even heard about it before. That’s why I got the idea for this blog post.</p>
<hr />
<h3>Introduction</h3>
<p>The <code>MutationObserver</code> interface provides the ability to watch for changes being made the <code>DOM</code> tree (source <a href="https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver" rel="noopener noreferrer external">MDN Web Docs</a>).</p>
<p>It is a web feature, natively implemented in all browsers (yes even Internet Explorer v11 according <a href="https://caniuse.com/?search=mutationobserver" rel="noopener noreferrer external">Caniuse</a>), which allows us to detect when changes are made to a document, to the web page.</p>
<hr />
<h3>In Other Words</h3>
<p>I dislike &quot;The Last stand&quot; movie but, do you remember when Rogue gets the vaccine (1) to remove her powers (2)? Without any other information, we still don't know if the cure was effective or not. To resolve the question (3), we would have to try our luck and get in contact but, without knowing what result to expect. On the other hand, thanks to his psychokinesis power, the professor X would him be able to detect the mutation (4) and knows if it worked out or not.</p>
<p>Our web page follows the same idea.</p>
<p>When we apply a modification to the DOM (1), such as modifying a tag or an attribute, with or without framework, it is interpreted and rendered by the browser (2). Even though the operation is really fast, if we query (3) the DOM elements touched by our changes right afterwards, we cannot be 100% sure that the modifications were already applied. Fortunately, thanks to the <code>MutationObserver</code>, we can detect the mutation (4) to get to know when and if it effectively worked out.</p>
<hr />
<h3>Walk-through</h3>
<p>To initialize a <code>MutationObserver</code> , you shall invoke its <code>constructor</code> with, as parameter, a <code>callback</code> function to be called when DOM changes occur.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> observer </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> MutationObserver</span><span style="color:#F8F8F2">(callback);</span></span></code></pre><p>The callback gets as parameter an array of the individual DOM mutations which have been applied.</p>
<p>To observe a targeted node and to begin receiving notification through the callback, you can invoke the function <code>observe()</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">observer.</span><span style="color:#50FA7B">observe</span><span style="color:#F8F8F2">(targetNode, config);</span></span></code></pre><p>As second parameter, a configuration shall be passed. It defines which kind of mutations we are looking to observe. These are documented on the excellent <a href="https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver/observe" rel="noopener noreferrer external">MDN Web Docs</a>. When it comes to me, I often use <code>attributes</code> to observe modifications to <code>style</code> and, <code>class</code> or, as in previous example, <code>childlist</code> to observe changes to the children of an element.</p>
<p>To stop the <code>MutationObserver</code> from receiving further notifications until and unless <code>observe()</code> is called again, the function <code>disconnect()</code> shall be used. It can be called within the callback or anywhere, as long as it is called on the instance.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">observer.</span><span style="color:#50FA7B">disconnect</span><span style="color:#F8F8F2">();</span></span></code></pre><p>Last but, not least, it exposes a function <code>takeRecords()</code> which can be queried to remove all pending notifications.</p>
<hr />
<h3>Concrete Example</h3>
<p>I was developing some improvements in the WYSIWYG inline editor of <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a> in which I had to apply a color to the user’s selection, entered via an input field, while preserving the range so that each time the user enters a new color, it would be applied to the same selected text 🤪.</p>
<p>Summarized something like following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">class</span><span style="color:#8BE9FD"> Cmp</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">      private</span><span style="color:#F8F8F2"> range </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> window.</span><span style="color:#50FA7B">getSelection</span><span style="color:#F8F8F2">()?.</span><span style="color:#50FA7B">getRangeAt</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">      applyColor</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> selection </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> window.</span><span style="color:#50FA7B">getSelection</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        selection?.</span><span style="color:#50FA7B">removeAllRanges</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">        selection?.</span><span style="color:#50FA7B">addRange</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.range);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> color </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> document.</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">input</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">).value;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        document.</span><span style="color:#50FA7B">execCommand</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">foreColor</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">false</span><span style="color:#F8F8F2">, color);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        this</span><span style="color:#F8F8F2">.range </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> selection?.</span><span style="color:#50FA7B">getRangeAt</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">      }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span></code></pre><p>It should have worked right? Well, no, it did not or at least not fully 😉.</p>
<p>Indeed, getting and applying the color to the selection did work as expected but, I was unable to save the range afterwards, <code>this.range</code> was not re-assigned as I was expecting.</p>
<p>Fortunately, I was able to solve the issue with the <code>MutationObserver</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">class</span><span style="color:#8BE9FD"> Cmp</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">      private</span><span style="color:#F8F8F2"> range </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> window.</span><span style="color:#50FA7B">getSelection</span><span style="color:#F8F8F2">()?.</span><span style="color:#50FA7B">getRangeAt</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">      applyColor</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> selection </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> window.</span><span style="color:#50FA7B">getSelection</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        selection?.</span><span style="color:#50FA7B">removeAllRanges</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">        selection?.</span><span style="color:#50FA7B">addRange</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.range);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> color </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> document.</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">input</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">).value;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">        // A. Create an observer</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> observer </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> MutationObserver</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">_mutations</span><span style="color:#FF79C6"> =></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#6272A4">            // D. Disconnect it when triggered as I only needed it once</span></span>
<span class="line"><span style="color:#F8F8F2">            observer.</span><span style="color:#50FA7B">disconnect</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#6272A4">            // E. Save the range as previously implemented</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">            this</span><span style="color:#F8F8F2">.range </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> selection?.</span><span style="color:#50FA7B">getRangeAt</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">        // B. Get the DOM element to observe</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> anchorNode </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> selection?.anchorNode;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">        // C. Observe 👀</span></span>
<span class="line"><span style="color:#F8F8F2">        observer.</span><span style="color:#50FA7B">observe</span><span style="color:#F8F8F2">(anchorNode, {childList</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        document.</span><span style="color:#50FA7B">execCommand</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">foreColor</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">false</span><span style="color:#F8F8F2">, color);</span></span>
<span class="line"><span style="color:#F8F8F2">      }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span></code></pre><p>First (A) I created a new <code>MutationObserver</code>. I defined which node element, in my case a parent one, had to be observed (B) and, I configured the observer (C) to begin receiving notifications through its callback function when DOM changes occurred. In the callback, I first disconnected (D) it, as only one event was interesting for my use case and finally (E) was able to save the range as expected 🥳.</p>
<hr />
<h3>Go Further</h3>
<p>If you liked this introduction about the <code>MutationObserver</code> , I can suggest you to go further and, have a look to the <a href="https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver" rel="noopener noreferrer external">ResizeObserver</a> and <a href="https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver" rel="noopener noreferrer external">IntersectionObserver</a>.</p>
<p>The first one can for example be used to detect changes to the size of editable fields and, the second one to lazy load content.</p>
<hr />
<h3>Summary</h3>
<p>You might not use the observers every day but, they are extremely useful when it comes to detecting changes applied to the DOM. In addition, it is fun to develop features with these 🤙.</p>
<p>To infinity and beyond!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[StorybookJS: Tips & Tricks]]></title>
          <description><![CDATA[Import Markdown files as Docs only pages, use a CDN to load dependencies & sort stories.]]></description>
          <link>https://daviddalbusco.com/blog/storybookjs-tips-and-tricks/</link>
          <pubDate>Wed, 05 May 2021 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*0yBFNNJGxc4vkhEPgV21aA.jpeg" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@florenciaviadana?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Florencia Viadana</a> on <a href="https://unsplash.com/s/photos/books?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I just migrated the <a href="https://docs.deckdeckgo.com" rel="noopener noreferrer external">documentation</a> of DeckDeckGo to <a href="https://storybook.js.org/" rel="noopener noreferrer external">StorybookJS</a>. More than its ability to simplify building and testing, I like to use it for documentation purpose because it allows me to integrate the README.md files that <a href="https://stenciljs.com/" rel="noopener noreferrer external">StencilJS</a> generates automatically. Sparring steps between code and documentation is the best don’t you think?</p>
<p>Here are a couple of tips &amp; tricks I reused, or discovered, along the process.</p>
<hr />
<h3>Import Markdown Files Into StorybookJS</h3>
<p>One particularly cool feature of StencilJS is that, out of the box, it auto-generates <code>readme.md</code> files in markdown from the code’s comments. Isn’t that neat?</p>
<p>I think it is. Therefore, to even push to concept further, I set up StorybookJS to import these Markdown files as Docs only pages. In that way, the documentation remains and, is edited as close as possible of the code and delivered to the end user without any interruption.</p>
<hr />
<h4>Meta</h4>
<p>At the time of writing the StorybookJS issue <a href="https://github.com/storybookjs/storybook/issues/11981" rel="noopener noreferrer external">#11981</a> list miscellaneous solutions, including using the <code>transcludeMarkdown</code> settings or raw loader. If following solution does not suit you, try one of these.</p>
<hr />
<h4>Limitation</h4>
<p>Even though it works very well, I did not manage to highlight the block of codes displayed in the pages and imported from Markdown files. I <a href="https://github.com/storybookjs/storybook/issues/11981#issuecomment-830158237" rel="noopener noreferrer external">commented</a> the issue accordingly.</p>
<p>If you manage to solve this, let me now or, even better, send me a <a href="https://github.com/deckgo/deckdeckgo/" rel="noopener noreferrer external">Pull Request</a> on GitHub 😉.</p>
<hr />
<h4>Solution</h4>
<p>I am using the HTML version of Storybook, I handle my stories in <code>.js</code> files as for example in a <code>Text.stories.js</code> in which I document a paragraph that accepts a background color as argument.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    title</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Components/Text</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    argTypes</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        bg</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { control</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">color</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> Text</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">bg</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F1FA8C"> `&#x3C;p style="background: </span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">bg</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">;"></span></span>
<span class="line"><span style="color:#F1FA8C">    Hello World</span></span>
<span class="line"><span style="color:#F1FA8C">  &#x3C;/p>`</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">Text.args </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    bg</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">#FF6900</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>According <a href="https://storybook.js.org/docs/react/writing-docs/docs-page" rel="noopener noreferrer external">StorybookJS</a>, we can replace the DocsPage template on a Component-Level to showcase our own documentation with MDX docs or a custom component. That’s why, next to our story we create a new file <code>Text.mdx</code> , which we import and provide as <code>page</code> to our story.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Doc } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./Text.mdx</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    title</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Components/Text</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    parameters</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        docs</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">            page</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> Doc</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    },</span></span>
<span class="line"><span style="color:#F8F8F2">    argTypes</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        bg</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { control</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">color</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> Text</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">bg</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F1FA8C"> `&#x3C;p style="background: </span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">bg</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">;"></span></span>
<span class="line"><span style="color:#F1FA8C">    Hello World</span></span>
<span class="line"><span style="color:#F1FA8C">  &#x3C;/p>`</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">Text.args </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    bg</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">#FF6900</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Finally, in our <code>.mdx</code> file, we import our <code>README.md</code> file (or any other Markdown files) and, we remix the DocsPage with a custom documentation, using the basic Storybook <code>Description</code> block.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Description } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@storybook/addon-docs/blocks</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> readme </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./readme.md</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> Doc</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Description</span><span style="color:#50FA7B;font-style:italic"> markdown</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">readme</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2"> />;</span></span></code></pre><p>That’s it, the Markdown file is integrated as Docs pages in StorybookJS 🥳.</p>
<hr />
<h4>Use A CDN To Load Dependencies</h4>
<p>Not sure anyone would ever had that requirements but, if like me, you would need to load dependencies from a CDN, here’s the trick: add your <code>script</code> to <code>./storybook/preview-head.html</code> . It will be evaluated with your stories.</p>
<p>Likewise, if you would like to define some <code>style</code> or load a specific Google Font for your components, you can modify the same file as well.</p>
<p>Some examples taken from my <a href="https://github.com/deckgo/deckdeckgo/blob/main/docs/.storybook/preview-head.html" rel="noopener noreferrer external">preview-head.html</a> file:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">link</span><span style="color:#50FA7B;font-style:italic"> rel</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">preconnect</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> href</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://fonts.gstatic.com</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">link</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    href</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://fonts.googleapis.com/css2?family=JetBrains+Mono&#x26;display=swap</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    rel</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">stylesheet</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">/></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">script</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    type</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">module</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://unpkg.com/@deckdeckgo/color@latest/dist/deckdeckgo-color/deckdeckgo-color.esm.js</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">style</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#FF79C6">    pre</span><span style="color:#FF79C6;font-style:italic">:</span><span style="color:#50FA7B;font-style:italic">not</span><span style="color:#FF79C6">(</span><span style="color:#50FA7B;font-style:italic">.prismjs</span><span style="color:#FF79C6">) > div</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD">        box-shadow</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> none</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">        margin</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 25</span><span style="color:#FF79C6">px</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">style</span><span style="color:#F8F8F2">></span></span></code></pre><hr />
<h4>Sort Stories</h4>
<p>A specific order for the stories can be defined in <code>./storybook/preview.js</code> using the property <code>storySort</code>. Each chapter have to be provided as <code>string</code> and their list of stories as an <code>array</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> theme </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./theme</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> parameters </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    actions</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { argTypesRegex</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">^on[A-Z].*</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> },</span></span>
<span class="line"><span style="color:#F8F8F2">    options</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        storySort</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">            order</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Introduction</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Introduction</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Getting Started</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">], </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Edit</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">HTML</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Lazy Loading</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">]]</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    },</span></span>
<span class="line"><span style="color:#F8F8F2">    docs</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        theme</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>The names should match these provided as <code>title</code> in the stories.</p>
<p>With <code>MDX</code> using <code>meta</code> :</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Meta } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@storybook/addon-docs/blocks</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">Meta</span><span style="color:#50FA7B;font-style:italic"> title</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Introduction/Getting Started</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> />;</span></span></code></pre><p>With <code>JS</code> through the default <code>title</code> :</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    title</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Components/Lazy Image</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    argTypes</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        imgSrc</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { control</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">text</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><hr />
<h3>Summary</h3>
<p><a href="https://stenciljs.com/" rel="noopener noreferrer external">StencilJS</a> + <a href="https://storybook.js.org/" rel="noopener noreferrer external">StorybookJS</a> =<br />
Awesome 💪</p>
<p>To infinity and beyond!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Bundling Figma Plugin With Esbuild]]></title>
          <description><![CDATA[Build a plugin for Figma with esbuild, the extremely fast JavaScript bundler.]]></description>
          <link>https://daviddalbusco.com/blog/bundling-figma-plugin-with-esbuild/</link>
          <pubDate>Tue, 20 Apr 2021 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*NNXUKFJSiPegQbuWq0f47g.jpeg" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@vargasuillian?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Uillian Vargas</a> on <a href="https://unsplash.com/s/photos/fast?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I recently published a new open source <a href="https://www.figma.com/community/plugin/950777256486678678/Figma-to-DeckDeckGo" rel="noopener noreferrer external">plugin</a> to export <a href="https://www.figma.com/" rel="noopener noreferrer external">Figma</a> frames to <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a> slides.</p>
<p>As I like to benefit from my experiences to learn and try new concept, instead of using a bundler as described in the Figma documentation, I decided to give a try to <a href="https://esbuild.github.io/" rel="noopener noreferrer external">esbuild</a>.</p>
<p>The least I can say, I loved it ❤️.</p>
<hr />
<h3>Foreword</h3>
<p>Following solution is the one I set up for my <a href="https://github.com/deckgo/figma-deckdeckgo-plugin" rel="noopener noreferrer external">plugin</a>. It does work like a charm but, notably because it was the first time I used esbuild, it might need some improvements. If you notice improvements or issues, let me know, I would like to hear from you!</p>
<p>Contributions to my plugin and PR are also welcomed 😉.</p>
<hr />
<h3>Setup</h3>
<p>In a Figma plugin, install both <code>esbuild</code> and <code>rimraf</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> i</span><span style="color:#F1FA8C"> esbuild</span><span style="color:#F1FA8C"> rimraf</span><span style="color:#BD93F9"> --save-dev</span></span></code></pre><p><code>rimraf</code> might not be needed, if you only build your project in a CI, nevertheless, for a local build, I think it is safer to delete the output directory before any new build.</p>
<p>In <code>package.json</code> add, or modify, the <code>build</code> script.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#E9F284">"</span><span style="color:#F1FA8C">scripts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">: {</span></span>
<span class="line"><span style="color:#8BE9FE">  "</span><span style="color:#8BE9FD">build</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">rimraf dist &#x26;&#x26; node ./esbuild.js</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>You might notice that the last command target a script called <code>esbuild.js</code>. This file will contain our bundling steps, therefore create such a new file at the root of your project.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">touch</span><span style="color:#F1FA8C"> esbuild.js</span></span></code></pre><p>Finally, in this newly created file, import <code>esbuild</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> esbuild </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> require</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">esbuild</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span></code></pre><hr />
<h3>Sandbox</h3>
<p>A Figma plugin run (see <a href="https://www.figma.com/plugin-docs/how-plugins-run/" rel="noopener noreferrer external">documentation</a>) in a combination of a sandbox, to access to the Figma nodes, and an iFrame, for the presentation layer. We set up firstly the sandbox’s build.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">// sandbox</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">esbuild</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">build</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        entryPoints</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">src/plugin.ts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">        bundle</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        platform</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">node</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        target</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">node10.4</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">        outfile</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">dist/plugin.js</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    })</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">catch</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> process.</span><span style="color:#50FA7B">exit</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">));</span></span></code></pre><p>In the above script, we bundle the <code>plugin.ts</code>, the sandbox’s code, to its JavaScript counterpart <code>plugin.js</code> . As configuration, we tell <code>esbuild</code> to treat it as a NodeJS <a href="https://esbuild.github.io/api/#platform" rel="noopener noreferrer external">platform</a> and we <a href="https://esbuild.github.io/api/#target" rel="noopener noreferrer external">target</a> the version 10.4.</p>
<blockquote>
<p>I configured my plugin to handle sources from a folder <code>src</code>. I also bundle the outcome to another one called <code>dist</code>. If you do not have the same structure, modify the solution accordingly.</p>
</blockquote>
<hr />
<h3>UI</h3>
<p>In comparison to the previous chapter, we are going to gather the results of the build instead of telling <code>esbuild</code> to write directly to a file. For such reason, we import NodeJS <code>fs</code> to interact with the file system.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> { readFile, writeFile } </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> require</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">fs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">).promises;</span></span></code></pre><p>We also install <code>html-minifier-terser</code> to minify the resulting HTML code.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> i</span><span style="color:#F1FA8C"> html-minifier-terser</span><span style="color:#BD93F9"> --save-dev</span></span></code></pre><p>Once installed, we add a related import to our build script too.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> minify </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> require</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">html-minifier-terser</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">).minify;</span></span></code></pre><p>These imports set, we implement the bundling.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">// iframe UI</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> script </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> esbuild.</span><span style="color:#50FA7B">buildSync</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        entryPoints</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">src/ui.ts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">        bundle</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        minify</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        write</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        target</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">chrome58</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">firefox57</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">safari11</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">edge16</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">]</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> html </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> readFile</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">src/ui.html</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">utf8</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> minifyOptions </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        collapseWhitespace</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        keepClosingSlash</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        removeComments</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        removeRedundantAttributes</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        removeScriptTypeAttributes</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        removeStyleLinkTypeAttributes</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        useShortDoctype</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        minifyCSS</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#50FA7B"> writeFile</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#E9F284">        "</span><span style="color:#F1FA8C">dist/ui.html</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F1FA8C">        `&#x3C;script></span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">script.outputFiles[</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">].text</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">&#x3C;/script></span><span style="color:#FF79C6">${</span><span style="color:#FF79C6">await</span><span style="color:#50FA7B"> minify</span><span style="color:#F8F8F2">(html</span><span style="color:#F1FA8C">, </span><span style="color:#F8F8F2">minifyOptions)</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">})();</span></span></code></pre><p>In the above script, we compile the <code>ui.ts</code> , our TypeScript code related to the UI, with <code>esbuild</code> . We instruct it to inline any imported dependencies into the file itself with the option <a href="https://esbuild.github.io/api/#bundle" rel="noopener noreferrer external">bundle</a>, we <a href="https://esbuild.github.io/api/#minify" rel="noopener noreferrer external">minify</a> the JavaScript code and, we do not <a href="https://esbuild.github.io/api/#write" rel="noopener noreferrer external">write</a> to the file system. Instead of such step, we gather the outcome in a variable I called <code>script</code> .</p>
<p>We read the <code>ui.html</code> source file, define some options for the HTML minification and, finally, write both compiled code and HTML to the output (<code>dist/ui.html</code> in this example).</p>
<hr />
<h3>Web Components</h3>
<p>Of course, I had to create some Web Components for my projects 😉. Integrating these follow same logic as previously, except that we use the esm <a href="https://esbuild.github.io/api/#format-commonjs" rel="noopener noreferrer external">format</a>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> buildWebComponents</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">entryPoints</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span></span>
<span class="line"><span style="color:#F8F8F2">    entryPoints</span></span>
<span class="line"><span style="color:#F8F8F2">        .</span><span style="color:#50FA7B">map</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">entryPoint</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span></span>
<span class="line"><span style="color:#F8F8F2">            esbuild.</span><span style="color:#50FA7B">buildSync</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">                entryPoints</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [entryPoint],</span></span>
<span class="line"><span style="color:#F8F8F2">                bundle</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">                minify</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">                write</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">                target</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">chrome58</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">firefox57</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">safari11</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">edge16</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">                format</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">esm</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">            })</span></span>
<span class="line"><span style="color:#F8F8F2">        )</span></span>
<span class="line"><span style="color:#F8F8F2">        .</span><span style="color:#50FA7B">map</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">componentScript</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> componentScript.outputFiles[</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">].text)</span></span>
<span class="line"><span style="color:#F8F8F2">        .</span><span style="color:#50FA7B">join</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">""</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> componentsScript </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> buildWebComponents</span><span style="color:#F8F8F2">([</span></span>
<span class="line"><span style="color:#E9F284">        "</span><span style="color:#F1FA8C">src/components/checkbox.ts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">        "</span><span style="color:#F1FA8C">src/components/button.ts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">        "</span><span style="color:#F1FA8C">src/components/spinner.ts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">        "</span><span style="color:#F1FA8C">src/components/fonts.ts</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    ]);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // Same as previous chapter</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#50FA7B"> writeFile</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#E9F284">        "</span><span style="color:#F1FA8C">dist/ui.html</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F1FA8C">        `&#x3C;script></span><span style="color:#FF79C6">${</span></span>
<span class="line"><span style="color:#F8F8F2">            script.outputFiles[</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">].text</span></span>
<span class="line"><span style="color:#FF79C6">        }</span><span style="color:#F1FA8C">&#x3C;/script>&#x3C;script type="module"></span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">componentsScript</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">&#x3C;/script></span><span style="color:#FF79C6">${</span><span style="color:#FF79C6">await</span><span style="color:#50FA7B"> minify</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#F8F8F2">            html</span><span style="color:#F1FA8C">,</span></span>
<span class="line"><span style="color:#F8F8F2">            minifyOptions</span></span>
<span class="line"><span style="color:#F8F8F2">        )</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">})();</span></span></code></pre><p>I created more than one Web Component (<code>checkbox.ts</code>, <code>button.ts</code>, etc.), that’s why the <code>buildWebComponents</code> function. It takes an array, a list of files, as parameter and, concat all bundle together to a single value.</p>
<p>And...that’s it 😃. Sandbox, UI and Web Components are bundled faster than ever ⚡️.</p>
<hr />
<h3>Repo</h3>
<p>You can find above solution and, other fun stuff in the open source repo of my plugin: <a href="https://github.com/deckgo/figma-deckdeckgo-plugin" rel="noopener noreferrer external">https://github.com/deckgo/figma-deckdeckgo-plugin</a></p>
<hr />
<h3>Summary</h3>
<p>Setting up a project with <a href="https://esbuild.github.io/" rel="noopener noreferrer external">esbuild</a> was a pleasant developer experience. Writing a JS script to bundle my project, without many dependencies and with much flexibility, definitely matches my current inspiration. In addition, the outcome, the bundling itself, is faaaaaaaaaaaaaast! I am looking forward to use this compiler in other projects 👍.</p>
<p>To infinity and beyond!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Turn Your Figma Designs Into Animated Slides]]></title>
          <description><![CDATA[Export your Figma frames to presentations with DeckDeckGo.]]></description>
          <link>https://daviddalbusco.com/blog/turn-your-figma-designs-into-animated-slides/</link>
          <pubDate>Thu, 08 Apr 2021 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>I am thrilled to unveil a new way to turn your Figma designs into animated slide decks thanks to the open source plugin I recently published: <a href="https://www.figma.com/community/plugin/950777256486678678/Figma-to-DeckDeckGo" rel="noopener noreferrer external">Figma to DeckDeckGo</a> 🥳.</p>
<iframe width="280" height="158" src="https://www.youtube.com/embed/arabVqr-1Do" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
<br/>
<hr />
<h3>But... Why?</h3>
<p>You may ask yourself why would you export your content from <a href="https://www.figma.com/" rel="noopener noreferrer external">Figma</a> to <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>? After-all it is possible to present a design without a 3rd party platform. I get that but, before making up your mind, let me list some advantages I do see in such a solution.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*-rC78uti7YtY_5rnCh7D7g.gif" alt="" / loading="lazy"></p>
<hr />
<h4>Animate, edit and share</h4>
<p>By importing your slides in DeckDeckGo you get a comprehensive way to chain and reorder your slides. Even though we do not offer yet a zillion of different options, transitions between these are going to be animated.</p>
<p>You can also decide if you want or not extract the text components of your frames. If you do so, these are going to remain editable, which is for example handy in case you have a last minute typo to correct or, if you would use a presentation again months after its creation and would have to modify an information which would not be up-to-date anymore.</p>
<p>Instead of sharing a PDF, you are going to transform your presentation to a standalone Progressive Web Apps, which can be share with your attendees easily. Beside the fact that doing so your deck remains dynamic, I do see also value here as you do not have to share a file of X megabytes but, only share a link to your optimized content.</p>
<hr />
<h4>Reuse your design</h4>
<p>The plugin splits the text of the design. The graphic components are exported in a single layer, in <code>webp</code> images. Again here, you can probably notice my obsession for performances. WebP images are smaller than their JPEG and PNG counterparts — usually on the magnitude of a 25–35% reduction in filesize (source <a href="https://web.dev/serve-images-webp/" rel="noopener noreferrer external">web.dev</a>).</p>
<p>These images, once imported in DeckDeckGo, are added to your collection of assets. This means that they can be (re)used across all your slides. You can then create and style slides with your existing images as background.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*OFXb-3GA8T3ZqqDZQcwIcQ.gif" alt="" / loading="lazy"></p>
<hr />
<h4>Add slides and live polls</h4>
<p>Decks you are importing from Figma are not written in stones, you are able to add slides to these using the variety of templates available in DeckDeckGo.</p>
<p>You can even engage your audience or class in real time by adding built-in live polls to your presentations. Your attendees will be able to contribute with their smartphones and, the results will be shown live.</p>
<iframe width="280" height="158" src="https://www.youtube.com/embed/nx4N07_6-x4" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
<br/>
<hr />
<h4>Remote control</h4>
<p>Finally, out of the box, there is a remote application, also open source, to control your presentations. Of course, it works out with the content you import from Figma too and, it does offer the exact same features (draw over your slides, display your notes, countdown, etc.).</p>
<iframe width="280" height="158" src="https://www.youtube.com/embed/tcO94-txZ2E" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
<br/>
<hr />
<h3>Open Source</h3>
<p>I would lie if I could say that the plugin is currently rock solid. I tested it successfully with different decks but, I expect that some improvements are going to be needed regarding the extraction of the texts.</p>
<p>The good thing is that you are not going to be blocked in case this process would be sluggish. You can always export your frames as images only and, still enjoy almost all other features.</p>
<p>For the rest, I count on you to help us shape and improve the plugin. Like any other parts of our platforms, it is open source! Ping me with your issues and best ideas in its related <a href="https://github.com/deckgo/figma-deckdeckgo-plugin" rel="noopener noreferrer external">GitHub repo</a>.</p>
<hr />
<h3>Code</h3>
<p>From a technical point of view, the plugin is developed with JavaScript, nothing fancy new here. Even though, it might be interesting because it is bundled with <a href="https://esbuild.github.io/" rel="noopener noreferrer external">esbuild</a> (which is not something documented by Figma) and contains some vanilla Web Components I created for its design (button, checkbox etc.). If those particular subjects are interesting to you, let me know, I would be happy to blog about it.</p>
<hr />
<h3>Summary</h3>
<p><img src="https://daviddalbusco.com/assets/images/1*QW4oGxrM0iLJjjPXoxJvAQ.gif" alt="" / loading="lazy"></p>
<p>This plugin is my first ever Figma plugin and needless to say, it was super fun to develop. I am very happy to launch this new feature for our eco-system, <a href="https://www.figma.com/community/plugin/950777256486678678/Figma-to-DeckDeckGo" rel="noopener noreferrer external">give it a try</a> 🤗.</p>
<p>To infinity and beyond!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Stencil Component Translations]]></title>
          <description><![CDATA[A quick way to set up i18n for Stencil components without dependencies.]]></description>
          <link>https://daviddalbusco.com/blog/stencil-component-translations/</link>
          <pubDate>Mon, 05 Apr 2021 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*NXe3RbssR10n4RGMVGoZVg.jpeg" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@lucasgwendt?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Lucas George Wendt</a> on <a href="https://unsplash.com/s/photos/internationalization?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I have been using the same strategy to internationalize quickly <a href="https://stenciljs.com/" rel="noopener noreferrer external">Stencil</a> components without dependencies in various projects. Among others for the project <a href="https://owlly.ch/" rel="noopener noreferrer external">Owlly</a> or the <a href="https://bonjour.help/" rel="noopener noreferrer external">Bonjour foundation</a>.</p>
<p>As all these projects are open source, I will share with you my recipe 🧑‍🍳.</p>
<hr />
<h3>Goal</h3>
<p>This blog post has not for goal to set up i18n for an application, design system or any other sort of projects from a certain scale. I might blog about this some day too, as I recently internationalized with the help of the community our project <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>, but, this article has for goal to add translations to a relatively small component or set of components without dependency.</p>
<p>I use this solution when I create components which contain some <code>slot</code>with default values and which have has primary market our lovely “four languages + english per default” Switzerland 🇨🇭.</p>
<hr />
<h3>Setup</h3>
<p>In your project create a new utility <code>translations.util.ts</code> and add the declarations.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">interface</span><span style="color:#8BE9FD;font-style:italic"> Translation</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  [</span><span style="color:#FFB86C;font-style:italic">key</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">]</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">interface</span><span style="color:#8BE9FD;font-style:italic"> Translations</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  de</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Translation</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  en</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Translation</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>For this example, I will “only” use German and English. For a real life use case, extend it with your requirements.</p>
<p>I declared an <code>interface Translation</code> but, it can be replaced with a TypeScript <code>Record&lt;string, string&gt;</code> . Result is the same, as you rather like.</p>
<p>Following the declarations, add a constant for the default (fallback) language and a list of supported languages.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> DEFAULT_LANGUAGE</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">en</span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> =</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">en</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> SUPPORTED_LANGUAGES</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">[] </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">de</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">en</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">];</span></span></code></pre><p>Finally, add the effective translations.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> TRANSLATIONS</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Translations</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  de</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    question</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">Wie fühlen Sie sich heute?</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    super</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">Sehr gut</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    bad</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">Nicht gut</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">  },</span></span>
<span class="line"><span style="color:#F8F8F2">  en</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    question</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">How do you feel today?</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    super</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">Super</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    bad</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">Bad</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>In this solution, as my goal is to keep it quick and easy, I code the translations.</p>
<p>It is possible to handle these in separate <code>json</code> files and <code>import</code> these dynamically at runtime. That’s two features I have developed for my more complex use case. After all I maybe really need to blog about this? Ping me if you think that would be an interesting subject!</p>
<hr />
<h3>Detect Languages</h3>
<p>I did not reinvent the wheel. I had a look to the widely use open source library <a href="https://github.com/ngx-translate/core" rel="noopener noreferrer external">ngx-translate</a> and implemented its detection of the browser languages.</p>
<p>In the same file, add the following function and initialization of the default language.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> initBrowserLang</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> =></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">typeof</span><span style="color:#F8F8F2"> window </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">undefined</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#FF79C6">      ||</span><span style="color:#FF79C6"> typeof</span><span style="color:#F8F8F2"> window.navigator </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">undefined</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  let</span><span style="color:#F8F8F2"> browserLang</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#F8F8F2">    window.navigator.languages</span></span>
<span class="line"><span style="color:#FF79C6">    &#x26;&#x26;</span><span style="color:#F8F8F2"> window.navigator.languages.length </span><span style="color:#FF79C6">></span><span style="color:#BD93F9"> 0</span><span style="color:#FF79C6"> ?</span></span>
<span class="line"><span style="color:#F8F8F2">              window.navigator.languages[</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">] </span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> null</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">  // @ts-ignore</span></span>
<span class="line"><span style="color:#F8F8F2">  browserLang </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> browserLang </span><span style="color:#FF79C6">||</span><span style="color:#F8F8F2"> window.navigator.language </span><span style="color:#FF79C6">||</span><span style="color:#F8F8F2"> window.navigator.browserLanguage </span><span style="color:#FF79C6">||</span><span style="color:#F8F8F2"> window.navigator.userLanguage;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">typeof</span><span style="color:#F8F8F2"> browserLang </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">undefined</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> (browserLang.</span><span style="color:#50FA7B">indexOf</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">-</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">!==</span><span style="color:#FF79C6"> -</span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">    browserLang </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> browserLang.</span><span style="color:#50FA7B">split</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">-</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">)[</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">];</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> (browserLang.</span><span style="color:#50FA7B">indexOf</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">_</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">!==</span><span style="color:#FF79C6"> -</span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">    browserLang </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> browserLang.</span><span style="color:#50FA7B">split</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">_</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">)[</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">];</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#F8F8F2"> browserLang;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> browserLang</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> =</span><span style="color:#50FA7B"> initBrowserLang</span><span style="color:#F8F8F2">();</span></span></code></pre><hr />
<h3>Function</h3>
<p>The setup and detection are ready, we need a function to render the translations.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> translate </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#F8F8F2">             (</span><span style="color:#FFB86C;font-style:italic">key</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">customLang</span><span style="color:#FF79C6">?:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">de</span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> |</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">en</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> lang</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> customLang </span><span style="color:#FF79C6">||</span><span style="color:#F8F8F2"> browserLang;</span></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#F8F8F2"> TRANSLATIONS[lang </span><span style="color:#FF79C6">!==</span><span style="color:#BD93F9"> undefined</span></span>
<span class="line"><span style="color:#FF79C6">                      &#x26;&#x26;</span><span style="color:#BD93F9"> SUPPORTED_LANGUAGES</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">includes</span><span style="color:#F8F8F2">(lang) </span><span style="color:#FF79C6">?</span></span>
<span class="line"><span style="color:#F8F8F2">                         lang </span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> DEFAULT_LANGUAGE][key];</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>It either uses the browser or a parameter language, check it against the list of supported languages or fallback to the default language, and returns the related translations.</p>
<hr />
<h3>Usage</h3>
<p>In our component, the above function can be used to render a translation.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Component, h, Host } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@stencil/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { translate } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./translations.utils</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    tag</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">question</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    shadow</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> Question</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Host</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span><span style="color:#FF79C6">{</span><span style="color:#50FA7B">translate</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">question</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF79C6">slot</span><span style="color:#50FA7B;font-style:italic"> name</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">answer</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span><span style="color:#FF79C6">{</span><span style="color:#50FA7B">translate</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">super</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">de</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">slot</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">Host</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        );</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>It renders text, with or without specifying a language, and can be used with <code>slot</code> too.</p>
<hr />
<h3>Summary</h3>
<p><img src="https://daviddalbusco.com/assets/images/1*Ud0o0tsLPyP76VNbKw-mvg.gif" alt="" / loading="lazy"></p>
<p>That was my small quick recipe to set up i18n to a relatively small set of components. I hope it is useful and if you think I should share the more complex solution in another post, let me know.</p>
<p>To infinity and beyond!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Node.js: Unzip Async Await]]></title>
          <description><![CDATA[Unzip a .zip file in an asynchronous Node.js context.]]></description>
          <link>https://daviddalbusco.com/blog/nodejs_unzip_async_await/</link>
          <pubDate>Sun, 14 Mar 2021 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*aRAiHOPwSAGnffuFtAGghA.jpeg" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@flo_stk?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Florian Steciuk</a> on <a href="https://medium.com/s/photos/highway?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<hr />
<p>I am developing a new feature of <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a> for which I have to unzip a data in <a href="https://firebase.google.com/docs/functions" rel="noopener noreferrer external">Firebase Functions</a>.</p>
<p>It took more time than expected to code such a <a href="https://nodejs.org/" rel="noopener noreferrer external">Node.js</a> function, that’s why I am sharing this solution, hoping it might help you some day too 😇.</p>
<hr />
<h3>Unzipper</h3>
<p>Node.js provides a compression module <a href="https://nodejs.org/api/zlib.html" rel="noopener noreferrer external">Zlib</a> but, it does not support ZIP files. Luckily, we can use the library <a href="https://github.com/ZJONSSON/node-unzipper" rel="noopener noreferrer external">unzipper</a> to handle these.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> i</span><span style="color:#F1FA8C"> unzipper</span><span style="color:#BD93F9"> --save</span></span></code></pre><hr />
<h3>Unzip With Async Await</h3>
<p>My new feature reads and writes data uploaded in <a href="https://firebase.google.com/docs/storage" rel="noopener noreferrer external">Firebase Storage</a> through streams. I also develop my code with a promises (async / await) approach. Therefore, both have to coexist.</p>
<p>To narrow down the following example, I replaced the cloud storage with local files handled with file system streams (<code>fs</code> ).</p>
<p>The function <code>unzip</code> instantiates a stream on the <code>zip</code> data which is piped with <code>unzipper</code> . Each entry are iterated and piped themselves to writable outputs. Summarized: the zip is opened and each files it contains are extracted.</p>
<p><code>unzip</code> is called in a retro compatible top await function and, that’s basically it 🥳.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> { Parse } </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> require</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">unzipper</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> { createWriteStream, createReadStream } </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> require</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">fs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> unzip</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> stream </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> createReadStream</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/Users/david/data.zip</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">).</span><span style="color:#50FA7B">pipe</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">Parse</span><span style="color:#F8F8F2">());</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">reject</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        stream.</span><span style="color:#50FA7B">on</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">entry</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, (</span><span style="color:#FFB86C;font-style:italic">entry</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">            const</span><span style="color:#F8F8F2"> writeStream </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> createWriteStream</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`/Users/david/</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">entry.path</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#F8F8F2"> entry.</span><span style="color:#50FA7B">pipe</span><span style="color:#F8F8F2">(writeStream);</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"><span style="color:#F8F8F2">        stream.</span><span style="color:#50FA7B">on</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">finish</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, () </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> resolve</span><span style="color:#F8F8F2">());</span></span>
<span class="line"><span style="color:#F8F8F2">        stream.</span><span style="color:#50FA7B">on</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">error</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, (</span><span style="color:#FFB86C;font-style:italic">error</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> reject</span><span style="color:#F8F8F2">(error));</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        await</span><span style="color:#50FA7B"> unzip</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#F8F8F2">        console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(err);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">})();</span></span></code></pre><hr />
<h3>Read To String With Async Await</h3>
<p>I had to read files with streams too. Consequently and cherry on top, here is how I integrated these in my code.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> { createReadStream } </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> require</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">fs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> read</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> stream </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> createReadStream</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/Users/david/meta.json</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">reject</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        let</span><span style="color:#F8F8F2"> data </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> ""</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        stream.</span><span style="color:#50FA7B">on</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">data</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, (</span><span style="color:#FFB86C;font-style:italic">chunk</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> (data </span><span style="color:#FF79C6">+=</span><span style="color:#F8F8F2"> chunk));</span></span>
<span class="line"><span style="color:#F8F8F2">        stream.</span><span style="color:#50FA7B">on</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">end</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, () </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> resolve</span><span style="color:#F8F8F2">(data));</span></span>
<span class="line"><span style="color:#F8F8F2">        stream.</span><span style="color:#50FA7B">on</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">error</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, (</span><span style="color:#FFB86C;font-style:italic">error</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> reject</span><span style="color:#F8F8F2">(error));</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> meta </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> read</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">({ meta });</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#F8F8F2">        console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(err);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">})();</span></span></code></pre><p>It follows the same approach as previously and read the file content to an in memory <code>string</code>.</p>
<hr />
<h3>Summary</h3>
<p><img src="https://daviddalbusco.com/assets/images/1*6wJfY8vH1FbC1s4Uzxy0QQ.gif" alt="" / loading="lazy"></p>
<blockquote>
<p>Coding is like a box of chocolates. You never know what you’re gonna get. Sometimes it should be quick, it takes time. Sometimes it should take so much time but, it goes fast — For-dev-rest Gump</p>
</blockquote>
<p>To infinity and beyond!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Tweet From GitHub Actions]]></title>
          <description><![CDATA[Develop a Twitter Bot that runs in GitHub Actions.]]></description>
          <link>https://daviddalbusco.com/blog/tweet-from-github-actions/</link>
          <pubDate>Fri, 05 Mar 2021 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*GGBgZEgWp8_bR5S2fVhybg.jpeg" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@ravinepz?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Ravi Sharma</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I launched recently <a href="https://discoverweekly.dev/" rel="noopener noreferrer external">DiscoverWeekly.dev</a> a website that shares each Wednesday the new music playlists made by the developers.</p>
<p>To spread the information and let people subscribe, in addition to a RSS feed, I opened a <a href="https://twitter.com/discoverweekly_" rel="noopener noreferrer external">Twitter</a> account and developed a Bot which tweets once a week about the news playlists as well.</p>
<p>Here is how you can also create a Twitter Bot that runs periodically in GitHub Actions.</p>
<hr />
<h3>Twitter API</h3>
<p>Tweets are posted with the help of the Twitter API. To get access to such a feature, you need to create a developer account and request access. You can start your application <a href="https://developer.twitter.com/en/apply-for-access" rel="noopener noreferrer external">here</a>.</p>
<p>The process takes several steps but, overall, can be solved in a couple of minutes.</p>
<p>Once you get access to your <a href="https://developer.twitter.com/en/portal/dashboard" rel="noopener noreferrer external">Dashboard</a>, create a new project.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*d7T5ulDZCk5AbJD5Qrwz-A.png" alt="" / loading="lazy"></p>
<p>In your “App permissions”, request the “Read and Write” permissions.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*NWATz8Zlxx3CgNZMmBvRVQ.png" alt="" / loading="lazy"></p>
<p>Finally, under “Keys and Tokens”, generate all “Access token &amp; secret”.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*axwweoz1kGPPaZW-I5h6Lg.png" alt="" / loading="lazy"></p>
<p>Note that if you have generated your secrets before requesting the &quot;read &amp; write&quot; permission, you have to regenerate your secrets.</p>
<hr />
<h3>Library</h3>
<p>Even though consuming the Twitter API does not look like rocket science, using an already well made Node.js library ease the process. In addition, Twitter is currently developing a new API (v2) but, the one we are looking to use, <a href="https://developer.twitter.com/en/docs/tweets/post-and-engage/api-reference/post-statuses-update" rel="noopener noreferrer external">statuses/update</a>, is not yet migrated (v1). Therefore, using a library as an extra layout might help us in that case not to have to rewrite everything in the future.</p>
<p>That is why we are adding the <a href="https://github.com/FeedHive/twitter-api-client" rel="noopener noreferrer external">twitter-api-client</a> library to our project.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> i</span><span style="color:#F1FA8C"> twitter-api-client</span><span style="color:#BD93F9"> --save-dev</span></span></code></pre><hr />
<h3>Twitter Bot</h3>
<p>To develop the Twitter Bot, we create a new file <code>twitter.js</code> at the root of our project. It contains a function which takes care of the <code>tweet</code> itself, using the <code>twitter-api-client</code> , and a top level <code>async</code> block which triggers it when the script is run.</p>
<p>In order to not expose publicly the tokens we have created previously, we are accessing these through environments variables (see next chapter).</p>
<p>Finally, in following example, the tweet is a constant message. As any tweet, should be maximal 280 characters long, can tag usernames and point to Url.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> { TwitterClient } </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> require</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">twitter-api-client</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> tweet</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">status</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> twitterClient </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> TwitterClient</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        apiKey</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> process.env.</span><span style="color:#BD93F9">TWITTER_API_KEY</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        apiSecret</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> process.env.</span><span style="color:#BD93F9">TWITTER_API_SECRET</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        accessToken</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> process.env.</span><span style="color:#BD93F9">TWITTER_API_ACCESS_TOKEN</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        accessTokenSecret</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> process.env.</span><span style="color:#BD93F9">TWITTER_ACCESS_TOKEN_SECRET</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#F8F8F2"> twitterClient.tweets.</span><span style="color:#50FA7B">statusesUpdate</span><span style="color:#F8F8F2">({ status });</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> myTweet </span><span style="color:#FF79C6">=</span><span style="color:#F1FA8C"> `Checkout @discoverweekly_ https://discoverweekly.dev`</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        await</span><span style="color:#50FA7B"> tweet</span><span style="color:#F8F8F2">(myTweet);</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#F8F8F2">        console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(err);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">})();</span></span></code></pre><p>To run the Bot, we add a related <code>scripts</code> target in our <code>package.json</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#E9F284">"</span><span style="color:#F1FA8C">scripts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">: {</span></span>
<span class="line"><span style="color:#8BE9FE">  "</span><span style="color:#8BE9FD">twitter</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">node ./twitter.js</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">},</span></span></code></pre><hr />
<h3>Environment Variables</h3>
<p>To make our tokens and secrets, we are handling through environment variables, available to our GitHub Actions, go to your <code>GitHub repo &gt; Settings &gt; Secrets</code> and add these with their respective values.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*p_SW7X4ifZ0tiONf0PsEDA.png" alt="" / loading="lazy"></p>
<hr />
<h3>GitHub Actions</h3>
<p>Finally, add a GitHub Actions such as <code>.github/workflows/twitter.yml</code> to your project which run the Node.js script we created before periodically.</p>
<p>In my case, as I publish once a week new playlists and tweet about these only once too, the workflow is <a href="https://docs.github.com/en/actions/reference/events-that-trigger-workflows#schedule" rel="noopener noreferrer external">scheduled</a> to run at specific UTC times.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#8BE9FD">name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> Twitter bot</span></span>
<span class="line"></span>
<span class="line"><span style="color:#BD93F9">on</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">  schedule</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#FF79C6">    -</span><span style="color:#8BE9FD"> cron</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">0 15 * * 3</span><span style="color:#E9F284">"</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">jobs</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">  build</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">    name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> Tweet about the new playlists</span></span>
<span class="line"><span style="color:#8BE9FD">    runs-on</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> ubuntu-latest</span></span>
<span class="line"><span style="color:#8BE9FD">    steps</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#FF79C6">      -</span><span style="color:#8BE9FD"> name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> Checkout Repo</span></span>
<span class="line"><span style="color:#8BE9FD">        uses</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> actions/checkout@master</span></span>
<span class="line"><span style="color:#FF79C6">      -</span><span style="color:#8BE9FD"> name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> Use Node.js</span></span>
<span class="line"><span style="color:#8BE9FD">        uses</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> actions/setup-node@v1</span></span>
<span class="line"><span style="color:#8BE9FD">        with</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">          node-version</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">14.x</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#FF79C6">      -</span><span style="color:#8BE9FD"> name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> Install Dependencies</span></span>
<span class="line"><span style="color:#8BE9FD">        run</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> npm ci</span></span>
<span class="line"><span style="color:#FF79C6">      -</span><span style="color:#8BE9FD"> name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> Run Twitter bot</span></span>
<span class="line"><span style="color:#8BE9FD">        run</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> npm run twitter</span></span>
<span class="line"><span style="color:#8BE9FD">        env</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">          TWITTER_API_KEY</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> ${{ secrets.TWITTER_API_KEY }}</span></span>
<span class="line"><span style="color:#8BE9FD">          TWITTER_API_SECRET</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> ${{ secrets.TWITTER_API_SECRET }}</span></span>
<span class="line"><span style="color:#8BE9FD">          TWITTER_API_ACCESS_TOKEN</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> ${{ secrets.TWITTER_API_ACCESS_TOKEN }}</span></span>
<span class="line"><span style="color:#8BE9FD">          TWITTER_ACCESS_TOKEN_SECRET</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }}</span></span></code></pre><hr />
<h3>Summary</h3>
<p><img src="https://daviddalbusco.com/assets/images/1*N4H7Sfn1DEt9XJWQ3PnnEA.gif" alt="" / loading="lazy"></p>
<p>I was surprised to notice how straight forward it was to develop such a Twitter Bot and it was actually kind of fun. I hope this article will help you too.</p>
<p>To infinity and beyond!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Introducing DiscoverWeekly.dev: The Playlists Made By Devs]]></title>
          <description><![CDATA[I developed an open source website to discover new music on a weekly basis without an algorithm.]]></description>
          <link>https://daviddalbusco.com/blog/introducing-discoverweekly-dot-dev-the-playlists-made-by-devs/</link>
          <pubDate>Mon, 22 Feb 2021 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*GFDCqLv-kxGco-IWWlwtJw.png" alt="" / loading="lazy"></p>
<p>Like everyone in the current situation, I miss being able to do a lot of different things such as hugging my mum, attending live events or being able to travel.</p>
<p>Recently, I also noticed I began to miss some immaterial things too, notably discovering new music to listen to totally unpredictably and not through an algorithm. Those bands and songs for which I fall in love for no other particular reason than the fact that they remind me a good memory.</p>
<p><a href="https://www.youtube.com/watch?v=e1dt57Uq2hU&amp;list=PL0B8FFFE57D5024AE" rel="noopener noreferrer external">The Upsetters</a>? Discovered at the <a href="https://www.iq-bar.com/" rel="noopener noreferrer external">IQ Bar</a> in Zürich, end of Summer when these were still open. We were having a drink with my friend Anatole. The background music was spreading a chill vibe in the all place. I ordered the vinyl as soon as I went back home.</p>
<p><a href="https://www.youtube.com/watch?v=RN9NC4iQcsA" rel="noopener noreferrer external">Violent Soho</a>? I am their biggest fan because an Australian guy, Jake, who I met in Bali, recommend them to me once I told him I just visited Brisbane.</p>
<p><a href="https://www.youtube.com/watch?v=vd_G92JIIaw" rel="noopener noreferrer external">Skuggan av Svampen</a>? I met a Swede called Seamus in Ahangama, Sri Lanka. We were surf buddies for a week and, we chit chatted about music. It turned out he was also a MC.</p>
<p>That’s why, I created <a href="https://discoverweekly.dev/" rel="noopener noreferrer external">DiscoverWeekly.dev</a>, a place where developers can share their favorite music by <a href="https://github.com/peterpeterparker/discoverweekly.dev#contributing" rel="noopener noreferrer external">contributing</a> to an open source repo.</p>
<iframe width="280" height="158" src="https://www.youtube.com/embed/uohpcHeR_E8" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
<br/>
<hr />
<h3>Contributing</h3>
<p>To share your favorite playlists, bands or songs, those you like to hear when you are coding or, those you simply just like to hear any time, you can submit your tips through a Pull Request.</p>
<blockquote>
<p>This process is really similar to the one of <a href="https://tiny-helpers.dev/" rel="noopener noreferrer external">Tiny Helpers.dev</a>, thank you <a href="https://twitter.com/stefanjudis" rel="noopener noreferrer external">Stefan Judis</a> for inspiration.</p>
</blockquote>
<p>First, clone and fork the <a href="https://github.com/peterpeterparker/discoverweekly.dev" rel="noopener noreferrer external">repository</a>. Then, either create manually a new <code>Markdown</code> file or, if you have a recent version of <a href="https://nodejs.org/en/" rel="noopener noreferrer external">Node.js installed</a>, head over to your terminal and run the following command:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">git clone git@github.com:[</span><span style="color:#FF79C6">YOUR_USERNAME</span><span style="color:#F8F8F2">]/discoverweekly.dev.git</span></span>
<span class="line"><span style="color:#F8F8F2">cd discoverweekly.dev</span></span>
<span class="line"><span style="color:#F8F8F2">npm ci</span></span>
<span class="line"><span style="color:#F8F8F2">npm run add:playlist</span></span></code></pre><p>Continue by editing your contribution, commit the changes and open a <a href="https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request" rel="noopener noreferrer external">pull request</a>.</p>
<p>The playlist you are submitting should contain some <code>meta</code> information about yourself, it is important to me to give back credits on the website to the contributors, and your music tips. You can either provide <code>Youtube</code> or <code>Spotify</code> urls.</p>
<p>All details regarding these information and formatting are described in the <a href="https://github.com/peterpeterparker/discoverweekly.dev/blob/main/CONTRIBUTING.md" rel="noopener noreferrer external">CONTRIBUTING.md</a>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">---</span></span>
<span class="line"><span style="color:#8BE9FD">date</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">2021-02-23</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#8BE9FD">name</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">David Dal Busco</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#8BE9FD">description</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Hello, I'm David, a freelance web developer 🤓</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#8BE9FD">profile</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">https://pbs.twimg.com/profile_images/..._400x400.jpg</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#8BE9FD">twitter</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">daviddalbusco</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#8BE9FD">github</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">peterpeterparker</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#8BE9FD">website</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">https://daviddalbusco.com/</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#8BE9FD">tags</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">#postmetal #electro</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">---</span></span>
<span class="line"></span>
<span class="line"><span style="color:#BD93F9;font-weight:bold"># Deafheaven</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">Their black metal in major cord is epic! Perfect to write a zillion lines of code. I have seen them three times live, hopefully one day at least a fourth time 🤞.</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">{% youtube sC3V6DU-o9k %}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#BD93F9;font-weight:bold">---</span></span>
<span class="line"></span>
<span class="line"><span style="color:#BD93F9;font-weight:bold"># Post French Touch</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">A playlist with my favorite electronic tracks made in France🇫🇷.</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">{% spotify playlist 3D6BESfLFj08osOqclOhFl %}</span></span></code></pre><hr />
<h3>Following</h3>
<p>You are obviously most welcome to check out a weekly basis the website (😇) but, you can also follow one of these channels if you want to stay up-to-date with the new shared playlists:</p>
<ul>
<li><a href="https://twitter.com/discoverweekly_" rel="noopener noreferrer external">Twitter</a></li>
<li><a href="https://discoverweekly.dev/rss.xml" rel="noopener noreferrer external">RSS feed</a></li>
<li><a href="https://open.spotify.com/playlist/1psyoVD1j3KOBBKADkJNat?si=a7ca738f5f42441d&amp;nd=1" rel="noopener noreferrer external">Spotify</a></li>
</ul>
<hr />
<h3>Summary</h3>
<p>There is always some uncertainty when starting a new side project whether someone will contribute or not. Fortunately here, some contributors already shared their playlists, submitted PR or reviewed the website. I am definitely fortunate to have such wonderful people in my network, thank you all for your help 🙏.</p>
<p>Looking forward to discover new music through your contributions.</p>
<p>To infinity and beyond!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[How To Download A File With Node.js]]></title>
          <description><![CDATA[If you ever need to download a file with Node.js, here is a small script to achieve your goal.]]></description>
          <link>https://daviddalbusco.com/blog/how-to-download-a-file-with-node-js/</link>
          <pubDate>Wed, 10 Feb 2021 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*D86BStQhZNADuM6GjmVVAQ.jpeg" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@sw_creates?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Savannah Wakefield</a> on <a href="https://unsplash.com/s/photos/download-file?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>Over the weekend I made a dependency update of my time tracking app “Tie Tracker” (<a href="https://tietracker.com" rel="noopener noreferrer external">PWA</a> / <a href="https://github.com/peterpeterparker/tietracker" rel="noopener noreferrer external">GitHub</a>).</p>
<p>In this particular tool, I defer the heavy work to <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers" rel="noopener noreferrer external">Web Workers</a> so that the UI does not find itself in a blocking state.</p>
<p>Because the app is meant to work offline and, is available in the <a href="https://apps.apple.com/us/app/tie-tracker/id1493399075" rel="noopener noreferrer external">App Store</a> and <a href="https://play.google.com/store/apps/details?id=com.tietracker.app" rel="noopener noreferrer external">Google Play</a>, I did not import the required workers’ dependencies through a CDN but, locally.</p>
<p>The app itself is developed with <a href="https://reactjs.org/" rel="noopener noreferrer external">React</a> but, I implemented the workers with vanilla JavaScript and no package manager to handle their dependencies.</p>
<p>Therefore, I had to come with a solution to update the libs with a<a href="https://nodejs.org/en/" rel="noopener noreferrer external">Node.js</a> script 😇.</p>
<hr />
<h3>Node Fetch</h3>
<p>There is no <code>window.fetch</code> like API in Node.js but, there is a light-weight module that brings such capabilities. That’s why I used <a href="https://github.com/node-fetch/node-fetch" rel="noopener noreferrer external">node-fetch</a> to implement the download of the file.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> i</span><span style="color:#F1FA8C"> node-fetch</span><span style="color:#BD93F9"> --save-dev</span></span></code></pre><hr />
<h3>Script</h3>
<p>The script I developed to update my dependencies is the following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> { createWriteStream } </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> require</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">fs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> { pipeline } </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> require</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">stream</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> { promisify } </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> require</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">util</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> fetch </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> require</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">node-fetch</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> download</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">url</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">path</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> streamPipeline </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> promisify</span><span style="color:#F8F8F2">(pipeline);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> response </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> fetch</span><span style="color:#F8F8F2">(url);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">response.ok) {</span></span>
<span class="line"><span style="color:#FF79C6">        throw</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Error</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`unexpected response </span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">response.statusText</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#50FA7B"> streamPipeline</span><span style="color:#F8F8F2">(response.body, </span><span style="color:#50FA7B">createWriteStream</span><span style="color:#F8F8F2">(path));</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        await</span><span style="color:#50FA7B"> download</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">            url</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">https://unpkg.com/...@latest/....min.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">            path</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./public/workers/libs/....min.js</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#F8F8F2">        console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(err);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">})();</span></span></code></pre><p>The above <code>download</code> function uses a stream pipeline to download a file, as displayed in the node-fetch README, and the built-in <code>fs</code> module to write the output to the file system.</p>
<p>Top Level Await is available as of Node.js v14.8.0 but, I used an immediate function because I integrated it in a chain in which it was not available yet.</p>
<p>That’s it 🥳</p>
<hr />
<h3>Continue Reading</h3>
<p>If you want to read more about React and Web Workers, I published back to back three blog posts about it last year 😉.</p>
<ul>
<li><a href="https://daviddalbusco.com/blog/react-and-web-workers" rel="noopener noreferrer external">React and Web Workers</a></li>
<li><a href="https://daviddalbusco.com/blog/react-web-workers-and-indexeddb" rel="noopener noreferrer external">React, Web Workers, and IndexedDB</a></li>
<li><a href="https://daviddalbusco.com/blog/react-web-workers-indexeddb-and-exceljs" rel="noopener noreferrer external">React, Web Workers, IndexedDB and ExcelJS</a></li>
</ul>
<p>To infinity and beyond!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Build Gatsby Websites Using Firestore Data (Without Plugin)]]></title>
          <description><![CDATA[Get Firestore data to build your Gatsby static website with flexibility.]]></description>
          <link>https://daviddalbusco.com/blog/build-gatsby-websites-using-firestore-data-without-plugin/</link>
          <pubDate>Thu, 21 Jan 2021 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*dFqYiAhtL5Z9RR-b2JArVQ.png" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@m2creates?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Melanie Magdalena</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>It is possible to populate <a href="https://www.gatsbyjs.com/" rel="noopener noreferrer external">Gatsby</a> websites using <a href="https://firebase.google.com/docs/firestore/" rel="noopener noreferrer external">Cloud Firestore</a> with existing plugins and CMS but, you might be looking to implement such solution by yourself, without dependencies, to get the full control on the process.</p>
<p>This was my goal when I recently developed the new website of <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>, more precisely when I was looking to present users’ published decks in the dedicated page “<a href="https://deckdeckgo.com/en/discover" rel="noopener noreferrer external">Discover</a>”.</p>
<p>In this article I will show you, step by step, how to collect Firestore data to build Gatsby static websites. In addition, as your data might content link to image, I will also show you how to optimize these.</p>
<hr />
<h3>Meta</h3>
<p>The current post was originally published in January 2021. It was developed using Gatsby v2 and, its source code is available on <a href="https://github.com/peterpeterparker/firestore-gatsby" rel="noopener noreferrer external">GitHub</a>.</p>
<hr />
<h3>Introduction</h3>
<p>The feature we are about to develop, takes place at <strong>build time</strong>. We are going to fetch data saved in a database, Firestore, to generate a static website with Gatsby. This process can be summarized in the following steps:</p>
<ol>
<li>Collect the Firestore data</li>
<li>Inject these in the GraphQL data source of Gastby</li>
<li>Optimize the images</li>
<li>Use the information to generate the site</li>
</ol>
<hr />
<h3>Sample Data</h3>
<p>To ease this tutorial and, to have a common point of reference, let’s define our goal to be the display of a list of dogs.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*zT7YJpdlsioaLZSLE2G0ug.png" alt="" / loading="lazy"></p>
<p>These are going to be fetched from a Firestore collection called <code>dogs</code>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">[</span></span>
<span class="line"><span style="color:#F8F8F2">    {</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">breed</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Beagle</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">img_url</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">https://images.dog.ceo/breeds/beagle/n02088364_16065.jpg</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">]</span></span></code></pre><p>Data and images are provided by the free and wonderful <a href="https://dog.ceo/dog-api/" rel="noopener noreferrer external">Dog API</a>.</p>
<hr />
<h3>Firestore</h3>
<p>Most of the other tutorials and posts I found regarding our subject, seems to use the <a href="https://firebase.google.com/docs/reference/admin/" rel="noopener noreferrer external">Admin SDK</a> to query the data locally or in the CI.</p>
<p>I am not a big fan of adding dependencies to my projects. In addition, if we are already using Firestore, we most certainly also have already functions in place. Therefore, instead of adding a dependency, we are going to fetch our data through a new <a href="https://firebase.google.com/docs/functions/http-events" rel="noopener noreferrer external">HTTP requests function</a>.</p>
<p>Such an approach has also the advantage to make our solution more portable. This tutorial can then be followed with any backend which would expose an API Endpoint to fetch data, not just Firestore.</p>
<p>That being said, at this point you may ask yourself: “But, David, if we do so, anyone can access our data?”.</p>
<p>To which I would answer: “Yes but, I’ve got your back”. Indeed, in a <a href="https://medium.com/better-programming/protect-your-http-firebase-cloud-functions-adf23c45765e" rel="noopener noreferrer external">previous article</a>, I shared a simple solution to protect HTTP functions with an authorization bearer.</p>
<p><em>For simplicity reason, I will not include such verification in following code snippets but, the related <a href="https://github.com/peterpeterparker/firestore-gatsby" rel="noopener noreferrer external">GitHub</a> repo does include it.</em></p>
<hr />
<h4>Function</h4>
<p>Given our data structure, we begin our implementation of the function by declaring an <code>interface</code> and a utility function (<code>findDogs)</code> to describe and query data.</p>
<p>No particular logic is applied to the query here. Nevertheless, it is worth to notice, that it de facto does offer all the flexibility you would need. If you would like to apply a filter, limit or order to your data, it is then possible to already applies these at that query level.</p>
<p>Finally, we expose the <code>function</code> and, return the data within the HTTP response.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#BD93F9"> *</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> functions </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">firebase-functions</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#BD93F9"> *</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> admin </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">firebase-admin</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> app </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> admin.</span><span style="color:#50FA7B">initializeApp</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">app.</span><span style="color:#50FA7B">firestore</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">settings</span><span style="color:#F8F8F2">({ timestampsInSnapshots</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2"> });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">interface</span><span style="color:#8BE9FD;font-style:italic"> Dog</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  id</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  breed</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  img_url</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> findDogs</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> snapshot </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> admin.</span><span style="color:#50FA7B">firestore</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">collection</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">dogs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">).</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">snapshot </span><span style="color:#FF79C6">||</span><span style="color:#FF79C6"> !</span><span style="color:#F8F8F2">snapshot.docs) {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> [];</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#F8F8F2"> snapshot.docs.</span><span style="color:#50FA7B">map</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">doc</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">      id</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> doc.id,</span></span>
<span class="line"><span style="color:#FF79C6">      ...</span><span style="color:#F8F8F2">doc.</span><span style="color:#50FA7B">data</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> Dog</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> dogs </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#F8F8F2">         functions.https.</span><span style="color:#50FA7B">onRequest</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">request</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">response</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> dogs</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Dog</span><span style="color:#F8F8F2">[] </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> findDogs</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    response.</span><span style="color:#50FA7B">json</span><span style="color:#F8F8F2">(dogs);</span></span>
<span class="line"><span style="color:#F8F8F2">  } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#F8F8F2">    response.</span><span style="color:#50FA7B">status</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">500</span><span style="color:#F8F8F2">).</span><span style="color:#50FA7B">json</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">      error</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> err,</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><p>Once deployed to Firebase (<code>firebase deploy --only functions:dogs</code> ), we can test our function with a <code>curl</code> command.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">#!/bin/sh</span></span>
<span class="line"><span style="color:#50FA7B">curl</span><span style="color:#BD93F9"> -i</span><span style="color:#BD93F9"> -H</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Accept: application/json</span><span style="color:#E9F284">"</span><span style="color:#BD93F9"> -X</span><span style="color:#F1FA8C"> GET</span><span style="color:#F1FA8C"> https://us-central1-yourproject.cloudfunctions.net/dogs</span></span></code></pre><hr />
<h3>Gatsby</h3>
<p>To gather data from an API and optimize remote images we, basically, are going to follow the Gatsby’s documentation about the creation of <a href="https://www.gatsbyjs.com/docs/how-to/plugins-and-themes/creating-a-source-plugin/" rel="noopener noreferrer external">source plugin</a>.</p>
<p>To integrate our feature, which should run once in the process of building our site, we code it in <code>gatsby-node.js</code> to take control on the data in the <a href="https://www.gatsbyjs.com/docs/reference/config-files/gatsby-node/" rel="noopener noreferrer external">GraphQL data layer</a>.</p>
<hr />
<h4>Environment</h4>
<p>In order to avoid copying and pasting, we add the url to our API in a <code>.env</code> file at the root of our project.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">API_URL</span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2">https:</span><span style="color:#6272A4">//us-central1-yourproject.cloudfunctions.net/dogs</span></span></code></pre><p>Such project environment variable is <a href="https://www.gatsbyjs.com/docs/how-to/local-development/environment-variables/" rel="noopener noreferrer external">not</a> immediately available in your Node.js scripts. That’s why we have first to add the following snippet to <code>gatsby-node.js</code> to load it.</p>
<hr />
<h4>Fetch</h4>
<p>Even though I don’t like to add dependencies to my projects, Node.js does not integrate the Fetch API in its standard. That’s why, anyway, we are adding <a href="https://github.com/node-fetch/node-fetch" rel="noopener noreferrer external">node-fetch</a> as a development dependency to our project.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> i</span><span style="color:#F1FA8C"> node-fetch</span><span style="color:#BD93F9"> --save-dev</span></span></code></pre><p>We use the extension point <a href="https://www.gatsbyjs.com/docs/reference/config-files/gatsby-node/#sourceNodes" rel="noopener noreferrer external">sourceNodes</a> to add our code as we are looking to create nodes to build our site.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> fetch </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> require</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">node-fetch</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">exports</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">sourceNodes</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">actions</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">createNodeId</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">createContentDigest</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> response </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> fetch</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">process.env.</span><span style="color:#BD93F9">API_URL</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">, {</span></span>
<span class="line"><span style="color:#F8F8F2">            method</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">POST</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">            headers</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">                Accept</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">application/json</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">                "</span><span style="color:#F1FA8C">Content-Type</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">application/json</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">response?.ok) {</span></span>
<span class="line"><span style="color:#F8F8F2">            console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Cannot fetch dogs data.</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> dogs </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> response.</span><span style="color:#50FA7B">json</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(dogs);</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#F8F8F2">        console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(err);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>At this point, if we build (<code>npm run build</code> ) our site, we should notice our data being fetched and printed to the terminal.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*lEJgBAxrFRBuzyfj9LpXOg.png" alt="" / loading="lazy"></p>
<hr />
<h4>Create Nodes</h4>
<p>To create Gatsby nodes that are queryable in our site, we iterate on the data, these we fetched with previous function, and, add an entry for each entity using the provided function <code>createNode</code> .</p>
<p>In addition, we define a constant for the node type which we are using to identify the data and which, we will also later on use to optimize the images.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> DOG_NODE_TYPE </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Dog</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> createNodes</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">actions</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">createNodeId</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">createContentDigest</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">dogs</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { createNode } </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> actions;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    dogs.</span><span style="color:#50FA7B">forEach</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">entry</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span></span>
<span class="line"><span style="color:#50FA7B">        createNode</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#FF79C6">            ...</span><span style="color:#F8F8F2">entry,</span></span>
<span class="line"><span style="color:#F8F8F2">            id</span><span style="color:#FF79C6">:</span><span style="color:#50FA7B"> createNodeId</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">DOG_NODE_TYPE</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">-</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">entry.id</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">),</span></span>
<span class="line"><span style="color:#F8F8F2">            parent</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> null</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">            children</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [],</span></span>
<span class="line"><span style="color:#F8F8F2">            internal</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">                type</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> `</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">DOG_NODE_TYPE</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">                content</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">stringify</span><span style="color:#F8F8F2">(entry),</span></span>
<span class="line"><span style="color:#F8F8F2">                contentDigest</span><span style="color:#FF79C6">:</span><span style="color:#50FA7B"> createContentDigest</span><span style="color:#F8F8F2">(entry)</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"><span style="color:#F8F8F2">        })</span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>If we now run our site in development (<code>npm run start</code> ), we can open up <code>http://localhost:8000/___graphql</code> and query your data.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">query MyQuery {</span></span>
<span class="line"><span style="color:#F8F8F2">    allDog {</span></span>
<span class="line"><span style="color:#F8F8F2">        edges {</span></span>
<span class="line"><span style="color:#F8F8F2">            node {</span></span>
<span class="line"><span style="color:#F8F8F2">                id</span></span>
<span class="line"><span style="color:#F8F8F2">                img_url</span></span>
<span class="line"><span style="color:#F8F8F2">                breed</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>If successful, we should find all the results delivered by the API.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*3ut-xNScba3910Ky2OIxiA.png" alt="" / loading="lazy"></p>
<hr />
<h4>Query And Display</h4>
<p>To query with GraphQL at build time, we use the <a href="https://www.gatsbyjs.com/docs/how-to/querying-data/use-static-query/" rel="noopener noreferrer external">hook</a> <code>useStaticQuery</code> . Because we do not have optimized the images yet, we only list our data.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#BD93F9"> *</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> React </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { graphql, useStaticQuery } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">gatsby</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> IndexPage</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> dogs </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useStaticQuery</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">graphql</span><span style="color:#F1FA8C">`</span></span>
<span class="line"><span style="color:#F1FA8C">        query DogQuery {</span></span>
<span class="line"><span style="color:#F1FA8C">            allDog {</span></span>
<span class="line"><span style="color:#F1FA8C">                nodes {</span></span>
<span class="line"><span style="color:#F1FA8C">                    id</span></span>
<span class="line"><span style="color:#F1FA8C">                    breed</span></span>
<span class="line"><span style="color:#F1FA8C">                }</span></span>
<span class="line"><span style="color:#F1FA8C">            }</span></span>
<span class="line"><span style="color:#F1FA8C">        }</span></span>
<span class="line"><span style="color:#F1FA8C">    `</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">main</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">>Doggos&#x3C;/</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">            {</span><span style="color:#F8F8F2">dogs.allDog.nodes.</span><span style="color:#50FA7B">map</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">dog</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF79C6">h2</span><span style="color:#50FA7B;font-style:italic"> key</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">dog.id</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span><span style="color:#FF79C6">{</span><span style="color:#F8F8F2">dog.breed</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">h2</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            ))</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#FF79C6">main</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> IndexPage;</span></span></code></pre><p>Such minimalistic component produce the following output which, however, confirm us the fact that we are able to fetch data from Firestore and, use these at build time in Gatsby 🥳.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*Ne96f5Ue7Mt40MPJRk_JtA.png" alt="" / loading="lazy"></p>
<hr />
<h4>Optimize Images</h4>
<p>Our data are link to images stored remotely. To download these in order to be able to work with them locally, we are using the official <a href="https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-source-filesystem" rel="noopener noreferrer external">gatsby-source-filesystem</a>plugin.</p>
<p>To integrate it in our feature, we add a function <code>onCreateNode</code> which will be called each time a node is created. That’s why we introduced a constant for the node type so that we only download those images we are interested in.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> { createRemoteFileNode } </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> require</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`gatsby-source-filesystem`</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">exports</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">onCreateNode</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">node</span><span style="color:#F8F8F2">, actions: { </span><span style="color:#FFB86C;font-style:italic">createNode</span><span style="color:#F8F8F2"> }, </span><span style="color:#FFB86C;font-style:italic">createNodeId</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">getCache</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (node.internal.type </span><span style="color:#FF79C6">===</span><span style="color:#F8F8F2"> DOG_NODE_TYPE) {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> fileNode </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> createRemoteFileNode</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">            url</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> node.img_url,</span></span>
<span class="line"><span style="color:#F8F8F2">            parentNodeId</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> node.id,</span></span>
<span class="line"><span style="color:#F8F8F2">            createNode,</span></span>
<span class="line"><span style="color:#F8F8F2">            createNodeId,</span></span>
<span class="line"><span style="color:#F8F8F2">            getCache</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (fileNode) {</span></span>
<span class="line"><span style="color:#F8F8F2">            node.remoteImage___NODE </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> fileNode.id;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>By assigning the field called <code>remoteImage___NODE</code> , Gatsby will be able to infer a connection between this field and the file node. This allows us to query the downloaded images.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">query MyQuery {</span></span>
<span class="line"><span style="color:#F8F8F2">  allDog {</span></span>
<span class="line"><span style="color:#F8F8F2">    nodes {</span></span>
<span class="line"><span style="color:#F8F8F2">      id</span></span>
<span class="line"><span style="color:#F8F8F2">      breed</span></span>
<span class="line"><span style="color:#F8F8F2">      remoteImage {</span></span>
<span class="line"><span style="color:#F8F8F2">        absolutePath</span></span>
<span class="line"><span style="color:#F8F8F2">      }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p><img src="https://daviddalbusco.com/assets/images/1*OYwkYIeElCDoNzuyMoXMAA.png" alt="" / loading="lazy"></p>
<p>To make the optimization of images possible at build time, we double check that the default <a href="https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-plugin-sharp" rel="noopener noreferrer external">gatsby-plugin-sharp</a> and <a href="https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-transformer-sharp" rel="noopener noreferrer external">gatsby-transformer-sharp</a> plugins are installed in our project.</p>
<p><em>If not, install these and, add them to your <code>gatsby-config.js</code>.</em></p>
<p>We also modify our query to verify that, indeed, images have been optimized by finding related <code>childImageSharp</code> nodes.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">query MyQuery {</span></span>
<span class="line"><span style="color:#F8F8F2">  allDog {</span></span>
<span class="line"><span style="color:#F8F8F2">    nodes {</span></span>
<span class="line"><span style="color:#F8F8F2">      id</span></span>
<span class="line"><span style="color:#F8F8F2">      breed</span></span>
<span class="line"><span style="color:#F8F8F2">      remoteImage {</span></span>
<span class="line"><span style="color:#F8F8F2">        childImageSharp {</span></span>
<span class="line"><span style="color:#F8F8F2">          id</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">      }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p><img src="https://daviddalbusco.com/assets/images/1*lX2FTYwV-pwfBZ8w8wCvXA.png" alt="" / loading="lazy"></p>
<p>Finally, we reflect the improvements to the GraphQL query in our component and, we render the images using the <a href="https://www.gatsbyjs.com/plugins/gatsby-image/" rel="noopener noreferrer external">gatsby-image</a> component.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#BD93F9"> *</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> React </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { graphql, useStaticQuery } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">gatsby</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> Img </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">gatsby-image</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> IndexPage</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> dogs </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useStaticQuery</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">graphql</span><span style="color:#F1FA8C">`</span></span>
<span class="line"><span style="color:#F1FA8C">        query DogQuery {</span></span>
<span class="line"><span style="color:#F1FA8C">            allDog {</span></span>
<span class="line"><span style="color:#F1FA8C">                nodes {</span></span>
<span class="line"><span style="color:#F1FA8C">                    id</span></span>
<span class="line"><span style="color:#F1FA8C">                    breed</span></span>
<span class="line"><span style="color:#F1FA8C">                    remoteImage {</span></span>
<span class="line"><span style="color:#F1FA8C">                        childImageSharp {</span></span>
<span class="line"><span style="color:#F1FA8C">                            id</span></span>
<span class="line"><span style="color:#F1FA8C">                            fluid {</span></span>
<span class="line"><span style="color:#F1FA8C">                                ...GatsbyImageSharpFluid</span></span>
<span class="line"><span style="color:#F1FA8C">                            }</span></span>
<span class="line"><span style="color:#F1FA8C">                        }</span></span>
<span class="line"><span style="color:#F1FA8C">                    }</span></span>
<span class="line"><span style="color:#F1FA8C">                }</span></span>
<span class="line"><span style="color:#F1FA8C">            }</span></span>
<span class="line"><span style="color:#F1FA8C">        }</span></span>
<span class="line"><span style="color:#F1FA8C">    `</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">main</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">>Doggos&#x3C;/</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">            {</span><span style="color:#F8F8F2">dogs.allDog.nodes.</span><span style="color:#50FA7B">map</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">dog</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF79C6">article</span><span style="color:#50FA7B;font-style:italic"> key</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">dog.id</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;</span><span style="color:#FF79C6">h2</span><span style="color:#F8F8F2">></span><span style="color:#FF79C6">{</span><span style="color:#F8F8F2">dog.breed</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">h2</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Img</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                        fluid</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">dog.remoteImage.childImageSharp.fluid</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                        alt</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">dog.breed</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                        style</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">{ width</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">100px</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> }</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#F8F8F2">                    /></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;/</span><span style="color:#FF79C6">article</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            ))</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#FF79C6">main</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> IndexPage;</span></span></code></pre><p>In addition to being displayed, our images should now appear with a “blur-up” effect while the images are being lazy-loaded.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*HcgSjYGHybfb6-IOe79B-Q.gif" alt="" / loading="lazy"></p>
<p><em>Gif slowed down to 0.25% of its original speed.</em></p>
<hr />
<h4>Development</h4>
<p>The solution is implemented 🎉. Nevertheless, we probably do not want to fetch data from Firestore while we are developing our site and thus, each time we restart our local server. Firebase <a href="https://cloud.google.com/firestore/pricing" rel="noopener noreferrer external">pricing model</a> being based on number of reads, writes, and deletes that we perform.</p>
<p>That’s why, I suggest to create a sample data at the root of the project. A file we can, for example, called <code>data.sample.json</code> . It should reflect the response provided by the API.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">[</span></span>
<span class="line"><span style="color:#F8F8F2">    {</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">id</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">HpW0clxI9uKLlhhk3q9E</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">img_url</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">https://images.dog.ceo/breeds/eskimo/n02109961_17033.jpg</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">breed</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Eskimo</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">]</span></span></code></pre><p>Finally, we can enhance our <code>createNodes</code> function to intercept the <code>development</code> environment and, read the sample data instead of querying the API.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> fetch </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> require</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">node-fetch</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> fs </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> require</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">fs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">exports</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">sourceNodes</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">actions</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">createNodeId</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">createContentDigest</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> activeEnv </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> process.env.</span><span style="color:#BD93F9">GATSBY_ACTIVE_ENV</span><span style="color:#FF79C6"> ||</span><span style="color:#F8F8F2"> process.env.</span><span style="color:#BD93F9">NODE_ENV</span><span style="color:#FF79C6"> ||</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">development</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (activeEnv </span><span style="color:#FF79C6">!==</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">production</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">            const</span><span style="color:#F8F8F2"> feed </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">parse</span><span style="color:#F8F8F2">(fs.</span><span style="color:#50FA7B">readFileSync</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">./dogs.sample.json</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">));</span></span>
<span class="line"><span style="color:#50FA7B">            createNodes</span><span style="color:#F8F8F2">(actions, createNodeId, createContentDigest, feed);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> response </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> fetch</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">process.env.</span><span style="color:#BD93F9">API_URL</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">, {</span></span>
<span class="line"><span style="color:#F8F8F2">            method</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">POST</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">            headers</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">                Accept</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">application/json</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">                "</span><span style="color:#F1FA8C">Content-Type</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">application/json</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">response?.ok) {</span></span>
<span class="line"><span style="color:#F8F8F2">            console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Cannot fetch dogs data.</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> dogs </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> response.</span><span style="color:#50FA7B">json</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">        createNodes</span><span style="color:#F8F8F2">(actions, createNodeId, createContentDigest, dogs);</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#F8F8F2">        console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(err);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><hr />
<h3>Take Away</h3>
<p>I personally find this solution interesting for static data or even for data, which do not change that often. In our project, I have set our deployment <a href="https://github.com/deckgo/deckdeckgo/blob/d74635cce3d1d85d1db7a4353268ab04a80380d8/.github/workflows/site.yml#L7" rel="noopener noreferrer external">GitHub actions</a> to run on publish and, once a week to ensure that, at least once a week, the last published presentations of our users made it to our showcase.</p>
<p>To infinity and beyond!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[A Little Scanner App Made With The Web]]></title>
          <description><![CDATA[I developed a little scanner prototype with the web during Christmas holidays to learn and try new skills.]]></description>
          <link>https://daviddalbusco.com/blog/a-little-scanner-app-made-with-the-web/</link>
          <pubDate>Mon, 04 Jan 2021 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*107_wVVJ8y3BVpo4SQKEkA.png" alt="" / loading="lazy"></p>
<hr />
<p>This last Christmas holidays, except visiting my parents for a couple of days, after having forced myself in a sort of auto-lockdown period first, I did not have any big plans. That’s why, I took the opportunity to improve my software development knowledge.</p>
<p>As I better learn concept by applying them to real application rather than writing examples, I decided to create a little scanner Progressive Web Apps entirely made with the web.</p>
<p>I called it <a href="https://rebelscan.com" rel="noopener noreferrer external">Rebel Scan</a> because, it is just a little scanner app, you rebel scum!</p>
<iframe width="280" height="158" src="https://www.youtube.com/embed/D3gfjqAo_Qs" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
<br/>
<p><em>A potato demo video of <a href="https://rebelscan.com" rel="noopener noreferrer external">Rebel Scan</a></em></p>
<hr />
<h3>Introduction</h3>
<p>It is important to note that this little scanner does not aim to be the most perfect scanner the world has ever seen. I am not even sure I will use it in the future. The image processing is not the best, there is no text extraction and on mobile it “only” shares PNG (see last chapter about it). It had absolutely no other goal that helping me learn new skills.</p>
<p>Therefore, before development begins, I defined the following objectives I never had tried before and, which I was eager to test:</p>
<ul>
<li>Give a try to <a href="https://nextjs.org/" rel="noopener noreferrer external">Next.js</a></li>
<li>Capture and crop a video stream using the <a href="https://developer.mozilla.org/fr/docs/Web/API/MediaDevices/getUserMedia" rel="noopener noreferrer external">MediaDevices.getUserMedia()</a> API</li>
<li>Generate <a href="https://reactjs.org/" rel="noopener noreferrer external">React</a> bindings for a Web Component developed with <a href="https://stenciljs.com/" rel="noopener noreferrer external">Stencil</a></li>
<li>Share files using strictly only the <a href="https://developer.mozilla.org/fr/docs/Web/API/Navigator/share" rel="noopener noreferrer external">Web Share API</a></li>
</ul>
<p>Without revealing the ending, I can confirm it was a success and, I was able to develop my little application. However, there was a couple of surprises along the way. Let’s tackle these, step by step.</p>
<h3>Meta</h3>
<p>The Progressive Web App, the result of this post and my experiment, is available online at <a href="https://rebelscan.com" rel="noopener noreferrer external">rebelscan.com</a> and its source code is available on <a href="https://github.com/peterpeterparker/rebelscan" rel="noopener noreferrer external">GitHub</a>.</p>
<hr />
<h3>Next.js</h3>
<p>Prior to this experiment and, thus since around a year, my favorite tech stack to implement websites was: <a href="https://gatsbyjs.com/" rel="noopener noreferrer external">Gatsby</a> for the development, <a href="https://github.com/features/actions" rel="noopener noreferrer external">GitHub actions</a> for deployment purpose and <a href="https://firebase.google.com/" rel="noopener noreferrer external">Firebase</a> as hosting.</p>
<p>Next.js by <a href="https://vercel.com" rel="noopener noreferrer external">Vercel</a>, without any surprise, was an excellent experience. I only scratched its surface and, I only used it to deploy a pre-rendered app but, it confirmed all the positive tweets and blogs I read about it.</p>
<p>It is really well <a href="https://nextjs.org/docs/getting-started" rel="noopener noreferrer external">documented</a> and getting started is straight forward. In comparison to Gatsby, I really liked that it took me absolutely no time to discover how to set up <a href="https://nextjs.org/docs/basic-features/typescript" rel="noopener noreferrer external">TypeScript</a> and, that the configuration seemed closer to a bare-bones dependency setup.</p>
<p>In addition, Next.js has a built-in support for internationalized (<a href="https://nextjs.org/docs/advanced-features/i18n-routing" rel="noopener noreferrer external">i18n</a>) routing. Needless to say, as a Swiss-French person, living in the Swiss-German part and writing posts in English, I greatly value such a feature 👍.</p>
<p>On the other side, without having done any research or statistics, I have the impression that the Gatsby’s ecosystem, in terms of plugins notably, is currently “wider”. As for example, with Gatsby there is one to generate automatically both sitemaps.xml and robots.txt where, with Next.js, it would need a bit more coding.</p>
<p>That being said, I am talking about peanuts. Both stacks are amazing and let me unleash performant websites and applications. I love them both ❤️.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*vSF3L96OuojzBPiExnpevg.gif" alt="" / loading="lazy"></p>
<hr />
<h3>MediaDevices.getUserMedia()</h3>
<p>Oh boy, this one gave me a hard time 😅.</p>
<p>The API itself is a wonderful piece of software and being able to capture a video stream with the Web is just amazing but, the feature I was looking to develop was a bit challenging regarding its responsiveness across devices. Indeed, my goal was to display the full video behind a cropped section (“two distinct elements”), without breaking points, and this regardless of the screen or camera size.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*Jt8uqpzR_IvqYwWNqdmIVg.png" alt="" / loading="lazy"></p>
<p>It ain’t probably rocket science for some but, to me, it was a bit challenging for the following reasons:</p>
<ol>
<li>You cannot use the video without HTTPS (from another device)</li>
<li>The video size is unpredictable</li>
<li>Finding the proper ratio and method took many iterations</li>
</ol>
<hr />
<h4>HTTPS</h4>
<p>It is possible to develop and test the camera stream locally but, as soon as you want to test it with your mobile phone, you need an HTTPS connection.</p>
<p>Therefore, I had two options. Either generate a certificate and run an HTTPS server on my laptop to which, I would connect from my device, or, use the “commit, push, test, try harder” method.</p>
<p>I let you check my commit history of the 2nd January 2020 to guess which method I went for 🤣.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*-B8CROTQuPCbhZAR3IjTwg.png" alt="" / loading="lazy"></p>
<hr />
<h4>The Video Size Is Unpredictable</h4>
<blockquote>
<p>Ask for portrait, gets landscape. Asks for landscape, gets portrait.</p>
</blockquote>
<p>Basically, you Android phone is like one of my best friend: he does what he wants, period.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> stream </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> navigator.mediaDevices.</span><span style="color:#50FA7B">getUserMedia</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    audio</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    video</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        width</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { ideal</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 1920</span><span style="color:#F8F8F2"> },</span></span>
<span class="line"><span style="color:#F8F8F2">        height</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { ideal</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 1080</span><span style="color:#F8F8F2"> },</span></span>
<span class="line"><span style="color:#F8F8F2">        facingMode</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">environment</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> [track] </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> stream.</span><span style="color:#50FA7B">getVideoTracks</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> settings </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> track.</span><span style="color:#50FA7B">getSettings</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">videoRef.current.width </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> settings.width;</span></span>
<span class="line"><span style="color:#F8F8F2">videoRef.current.height </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> settings.height;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// Android (portrait): &#x3C;video width="1080" height="1920"/></span></span></code></pre><hr />
<h4>Ratio</h4>
<p>It took me several iterations to find out the appropriate method and ratio(video to cropping section) to achieve my goal.</p>
<p>In a first version, it all worked out but, the quality of the scanned document was way to pixelated. In a second one, I did manage to improve it but, once applied on my phone, it felt like having an enormous zoom constantly added to the camera.</p>
<p>Finally, I figured out that I had both to handle portrait and landscape video sizes, crop these to my canvas and to find the exact CSS rules to make everything responsive for any devices.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> canvasPadding </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> 64</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">let</span><span style="color:#F8F8F2"> y </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> videoSize.height </span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2"> canvasPadding;</span></span>
<span class="line"><span style="color:#FF79C6">let</span><span style="color:#F8F8F2"> x </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> (y </span><span style="color:#FF79C6">*</span><span style="color:#BD93F9"> 210</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">/</span><span style="color:#BD93F9"> 297</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> maxWidth </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> videoSize.width </span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2"> canvasPadding;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">if</span><span style="color:#F8F8F2"> (x </span><span style="color:#FF79C6">></span><span style="color:#F8F8F2"> maxWidth) {</span></span>
<span class="line"><span style="color:#F8F8F2">    x </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> maxWidth;</span></span>
<span class="line"><span style="color:#F8F8F2">    y </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> (x </span><span style="color:#FF79C6">*</span><span style="color:#BD93F9"> 297</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">/</span><span style="color:#BD93F9"> 210</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> deltaX </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> (videoSize.width </span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2"> x) </span><span style="color:#FF79C6">/</span><span style="color:#BD93F9"> 2</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> deltaY </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> (videoSize.height </span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2"> y) </span><span style="color:#FF79C6">/</span><span style="color:#BD93F9"> 2</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> context </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> scanRef.current.</span><span style="color:#50FA7B">getContext</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">2d</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">context.</span><span style="color:#50FA7B">drawImage</span><span style="color:#F8F8F2">(videoRef.current, deltaX, deltaY, x, y, </span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">2100</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">2970</span><span style="color:#F8F8F2">);</span></span></code></pre><p>In above snippet, I calculate the section of the image, using a format A4, and I am looking to crop it while ensuring that the results won’t be larger than the video (remember, the size is unpredictable). If so, I do the contrary and take the width as maximum value. Finally, I draw my section in a 2D canvas with my expected ratio.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*eacFy349YzXyGycPboGkkw.jpeg" alt="" / loading="lazy"></p>
<p><em>Source: MDN Web Docs <a href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage" rel="noopener noreferrer external">CanvasRenderingContext2D.drawImage()</a></em></p>
<hr />
<h4>Takeaway</h4>
<p>It felt a bit like roller coaster of emotion developing this video conversion until I was finally able to implement my original idea but, I am happy with the results and, I will for sure reuse this technology in the future in news apps. The web is pretty slick (once in place 😉).</p>
<hr />
<h4>One more thing</h4>
<p>Oh and, “One more thing”: it might not work on iPhone even if it works like a charm with Safari on OSX. I just did a quick test with my (real) iPhone and it thrown an error.</p>
<blockquote>
<p>NotAllowedError: The request is not allowed by the user agent or the platform in the current context, possibly because the user denied permission.</p>
</blockquote>
<p>After some research, I found a Webkit <a href="https://webkit.org/blog/6784/new-video-policies-for-ios/" rel="noopener noreferrer external">post</a> explaining that video can, under certain rules, be automatically played on iOS. It did not work anyway therefore, I changed the UX on iPhone to start the process only after a user interaction.</p>
<p>Nevertheless, it did solve the video issue but, lead to another one. The video was not streamed in the cropped canvas.</p>
<p>As I did not find any solution, in last resort, I decided to play my last card: calling my mum to ask her to test the feature on her iPad 😉. On the phone, she tried it and, called then my dad to take photos of the device to send me these &quot;screenshots&quot; per WhatsApp 🤣.</p>
<p>To my surprise, it dit work out! Therefore, I came to the conclusion the iPhone with low memory cannot stream a video to canvas.</p>
<hr />
<h3>React bindings with Stencil</h3>
<p>If you read for the first time one of my blog post, you probably don’t know but, I am a big fan of Web Components and <a href="https://stenciljs.com/" rel="noopener noreferrer external">Stencil</a>.</p>
<p>Even if I created multiple libraries and applications, notably most of the eco-system of our open source editor for presentations, <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>, I never had tried before to generate bindings for frameworks 🤷.</p>
<p>Naturally, I thought that using this experiment was a good excuse to do so.</p>
<p>As any other Stencil things related, I have to say that once again, the all process was relatively easy. I basically “just” had to follow the <a href="https://stenciljs.com/docs/react" rel="noopener noreferrer external">documentation</a>.</p>
<p>The only aha moment I had regarding this subject happened when I finally understood that the bindings have to be packaged and, delivered through NPM by another project, another repo!</p>
<p>That’s why the “output target” are made for. They copy the definition’s file from the Web Component’s project to the bindings one.</p>
<p>Once every thing was in place and <a href="https://www.npmjs.com/package/web-photo-filter-react" rel="noopener noreferrer external">published</a>, I was able to use my <a href="https://github.com/peterpeterparker/web-photo-filter" rel="noopener noreferrer external">Web Component</a> to apply Instagram-like WebGL filters to photos in my experiment, in order to add the “scanning” effect, using a combination of desaturate, saturation and contrast.</p>
<p>Meantime, since I was already there, I also developed some improvements and breaking changes to the component. I even implemented (see that <a href="https://github.com/peterpeterparker/web-photo-filter/tree/webworker" rel="noopener noreferrer external">branch</a>) the usage of an <a href="https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas" rel="noopener noreferrer external">OffscreenCanvas</a> to defer the WebGL work to a web worker in order to improve the rendering in my little scanner. Unfortunately, I ultimately decided to not unleash this feature. It is really well supported by Chrome but, only available behind a flag in Firefox and, not even implemented in Safari.</p>
<p>Checkout its <a href="https://webphotofilter.com/" rel="noopener noreferrer external">showcase</a>.</p>
<hr />
<h3>Share Files With Web Share API</h3>
<p>I used the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Navigator/share" rel="noopener noreferrer external">Web Share API</a> in the past but, never to share files. Because the results of the little scanner had to be PDF and, to be stored in the cloud, it was the perfect use case too.</p>
<p>Regarding this feature, I would lie if I would not tell you, I lost two good hours while implementing it. Not that the code or API was unclear, even if the Typescript definition does not handle yet the <code>files</code> option, but, I faced some weird errors at runtime. Even in Chrome which is known to be always top notch when it comes to API implementation.</p>
<blockquote>
<p>DOMException: Failed to execute ‘share’ on ‘Navigator’: Must be handling a user gesture to perform a share request.</p>
</blockquote>
<p>Finally, I figured out that these two following were my “mistakes”:</p>
<ol>
<li>The <code>files</code> to be share have to be <code>File</code> . It does not work with <code>Blob</code> .</li>
<li>Currently, PDF are <strong>not</strong> supported! I found out a <a href="https://docs.google.com/document/d/1tKPkHA5nnJtmh2TgqWmGSREUzXgMUFDL6yMdVZHqUsg/edit" rel="noopener noreferrer external">list</a> of supported formats, stored as a Google Doc (?), linked in the middle of a blog post of <a href="https://web.dev/web-share/" rel="noopener noreferrer external">web.dev</a>. After <a href="https://twitter.com/daviddalbusco/status/1344637324798398464" rel="noopener noreferrer external">tweeting</a> about it, it was confirmed it was a <a href="https://bugs.chromium.org/p/chromium/issues/detail?id=1006055" rel="noopener noreferrer external">known issue or request</a>.</li>
</ol>
<p>Well, since I could not share PDF, at least for now, I used PNG and, it worked out like a charm ultimately.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> shareImage</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">src</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> res </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> fetch</span><span style="color:#F8F8F2">(src);</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> blob </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> res.</span><span style="color:#50FA7B">blob</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> file </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> File</span><span style="color:#F8F8F2">([blob], </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">rebelscan.png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, { type</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">image/png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, lastModified</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> Date.</span><span style="color:#50FA7B">now</span><span style="color:#F8F8F2">() });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#F8F8F2"> navigator.</span><span style="color:#50FA7B">share</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#6272A4">        // @ts-ignore</span></span>
<span class="line"><span style="color:#F8F8F2">        files</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [file],</span></span>
<span class="line"><span style="color:#F8F8F2">        title</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Rebel Scan</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        url</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">https://rebelscan.com</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>On desktop, until Chrome supports it in its next version v89 (related <a href="https://www.chromestatus.com/feature/4777349178458112" rel="noopener noreferrer external">issue</a>), I implemented my “go to strategy” aka: if available, use the <a href="https://web.dev/file-system-access/" rel="noopener noreferrer external">File System API</a> and, if not, fallback on a good old school download.</p>
<p>In addition, my mum discovered that the files seems to not be yet implemented on iOS. At least on her iPad (I did not ask her which version), the share was successfully triggered but, no image was attached to the resulting email. That's why, I also implemented to &quot;old school&quot; download solution for such devices.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> savePdf</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">src</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> blob </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> convertToPdfBlob</span><span style="color:#F8F8F2">(src);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">showSaveFilePicker</span><span style="color:#E9F284">"</span><span style="color:#FF79C6"> in</span><span style="color:#F8F8F2"> window) {</span></span>
<span class="line"><span style="color:#FF79C6">        await</span><span style="color:#50FA7B"> saveFilesystem</span><span style="color:#F8F8F2">(blob);</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    download</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">rebelscan.pdf</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, blob);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"><span style="color:#6272A4">/* File System API */</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> saveFilesystem</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">content</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> fileHandle </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> getNewFileHandle</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#50FA7B"> writeFile</span><span style="color:#F8F8F2">(fileHandle, content);</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">function</span><span style="color:#50FA7B"> getNewFileHandle</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> opts </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        types</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#F8F8F2">            {</span></span>
<span class="line"><span style="color:#F8F8F2">                description</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">PDF</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">                accept</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#E9F284">                    "</span><span style="color:#F1FA8C">application/pdf</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">.pdf</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">]</span></span>
<span class="line"><span style="color:#F8F8F2">                }</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"><span style="color:#F8F8F2">        ]</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#50FA7B"> showSaveFilePicker</span><span style="color:#F8F8F2">(opts);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> writeFile</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">fileHandle</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">content</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> writer </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> fileHandle.</span><span style="color:#50FA7B">createWritable</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#F8F8F2"> writer.</span><span style="color:#50FA7B">write</span><span style="color:#F8F8F2">(content);</span></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#F8F8F2"> writer.</span><span style="color:#50FA7B">close</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"><span style="color:#6272A4">/* Old school is the new school (download) */</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> download</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">filename</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">blob</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> a </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> document.</span><span style="color:#50FA7B">createElement</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">a</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    a.style.display </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">none</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    document.body.</span><span style="color:#50FA7B">appendChild</span><span style="color:#F8F8F2">(a);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> url </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> window.</span><span style="color:#BD93F9">URL</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">createObjectURL</span><span style="color:#F8F8F2">(blob);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    a.href </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> url;</span></span>
<span class="line"><span style="color:#F8F8F2">    a.download </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> filename;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    a.</span><span style="color:#50FA7B">click</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    window.</span><span style="color:#BD93F9">URL</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">revokeObjectURL</span><span style="color:#F8F8F2">(url);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (a </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> a.parentElement) {</span></span>
<span class="line"><span style="color:#F8F8F2">        a.parentElement.</span><span style="color:#50FA7B">removeChild</span><span style="color:#F8F8F2">(a);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><hr />
<h3>Takeaway</h3>
<blockquote>
<p>It is all fun and game until you try it out on a real mobile device.</p>
</blockquote>
<p>I am joking (a bit 😅) with the above sentence but, it is a good reminder that any web applications should always be tested and, if possible, not simulated, on multiple devices.</p>
<p>On a personal point of view, I am really happy to have invested some times to learn these wonderful pieces of software and, I hope to reuse these learnings in my personal projects or those of my clients in the future.</p>
<p>Happy New Year 🎉</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[More Gatsby i18n Tips And Tricks]]></title>
          <description><![CDATA[A follow-up to my article about the internationalization of Gatsby websites.]]></description>
          <link>https://daviddalbusco.com/blog/more-gatsby-i18n-tips-and-tricks/</link>
          <pubDate>Thu, 24 Dec 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*pWteHjwMcqePrFNKqgq2ug.jpeg" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@purejulia?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">pure julia</a> on <a href="https://unsplash.com/s/photos/world-map?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>Earlier this year (2020), when I was publishing a blog post every day during the lockdown, I shared <a href="https://daviddalbusco.com/blog/internationalization-with-gatsby" rel="noopener noreferrer external">my solution</a> to internationalize website build with <a href="https://www.gatsbyjs.com/" rel="noopener noreferrer external">Gatsby</a>.</p>
<p>The past few days, I have re-build from the ground up the website of <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a> with the goals to extract static content from our editor for slides and, to develop the foundation to internationalize our platform.</p>
<p>At first, I was looking to experiment a new method but, did not find any which worked well for me. That’s why I developed this new website with Gatsby using my own recipe, again.</p>
<p>Doing so, I learned a couple of new tricks and also improved, I hope, the solution.</p>
<hr />
<h3>Meta</h3>
<p>This article is a follow-up to my March 2020 blog post: <a href="https://daviddalbusco.com/blog/internationalization-with-gatsby" rel="noopener noreferrer external">Internationalization With Gatsby</a>.</p>
<p>The current post was written in December 2020 using Gatsby v2, <a href="https://github.com/angeloocana/gatsby-plugin-i18n" rel="noopener noreferrer external">gatsby-plugin-i18n</a> v1, <a href="https://reactjs.org/" rel="noopener noreferrer external">React</a> v17 and <a href="https://github.com/formatjs/formatjs" rel="noopener noreferrer external">react-intl</a> v5.</p>
<p>Its code snippets are taken from our open source website on <a href="https://github.com/deckgo/deckdeckgo/tree/master/site" rel="noopener noreferrer external">GitHub</a>.</p>
<hr />
<h3>JSON Translation Files</h3>
<p>In the previous article, I was relying on JavaScript files to handle translations (<code>i18n/en.js</code>).</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#8BE9FD;font-style:italic">module</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">exports</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    hello</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Hello world</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Needless to say, I was never a big fan of such method. That’s why in our new website, I replaced these by JSON data (<code>i18n/en.json</code>).</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">hello</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Hello world</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>These JSON files can then be imported in their respective language <code>Layout</code> component as I used to do with JS import (<code>layout/en.js</code> ).</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> React </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> Layout </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./layout</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// Previously with JS</span></span>
<span class="line"><span style="color:#6272A4">// import messages from '../../i18n/en';</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// New with JSON</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> messages </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">../../i18n/en.json</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@formatjs/intl-pluralrules/locale-data/en</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">props</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Layout</span><span style="color:#FF79C6"> {...</span><span style="color:#F8F8F2">props</span><span style="color:#FF79C6">}</span><span style="color:#50FA7B;font-style:italic"> messages</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">messages</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2"> />;</span></span></code></pre><p>I did not make any changes in the common <code>Layout</code> component itself. It still declares the layout and wrap the children in a <code>IntlProvider</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> React </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { useStaticQuery, graphql } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">gatsby</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { IntlProvider } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">react-intl</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@formatjs/intl-pluralrules/polyfill</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { getCurrentLangKey } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">ptz-i18n</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">children</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">location</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">messages</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> data </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useStaticQuery</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">graphql</span><span style="color:#F1FA8C">`</span></span>
<span class="line"><span style="color:#F1FA8C">        query SiteTitleQuery {</span></span>
<span class="line"><span style="color:#F1FA8C">            site {</span></span>
<span class="line"><span style="color:#F1FA8C">                siteMetadata {</span></span>
<span class="line"><span style="color:#F1FA8C">                    title</span></span>
<span class="line"><span style="color:#F1FA8C">                    languages {</span></span>
<span class="line"><span style="color:#F1FA8C">                        defaultLangKey</span></span>
<span class="line"><span style="color:#F1FA8C">                        langs</span></span>
<span class="line"><span style="color:#F1FA8C">                    }</span></span>
<span class="line"><span style="color:#F1FA8C">                }</span></span>
<span class="line"><span style="color:#F1FA8C">            }</span></span>
<span class="line"><span style="color:#F1FA8C">        }</span></span>
<span class="line"><span style="color:#F1FA8C">    `</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { langs, defaultLangKey } </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> data.site.siteMetadata.languages;</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> langKey </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> getCurrentLangKey</span><span style="color:#F8F8F2">(langs, defaultLangKey, location.pathname);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IntlProvider</span><span style="color:#50FA7B;font-style:italic"> locale</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">langKey</span><span style="color:#FF79C6">}</span><span style="color:#50FA7B;font-style:italic"> messages</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">messages</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#FF79C6">            {</span><span style="color:#F8F8F2">children</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IntlProvider</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><hr />
<h3>Multiple JSON Data</h3>
<p>Don’t know if this idiom exists in English but, in French, with can say something such as “Don’t put all the eggs in the same basket”. Indeed, we might not want to handle all our translations in a single file but, split these in multiple data.</p>
<p>Thanks to JSON, I was able to resolve this need quite easily. For example, I created two separate files <code>i18n/index.json</code> and <code>i18n/common.json</code> .</p>
<p>Important to notice, I prefixed the translations’ keys with keywords, such as <code>index</code> or <code>common</code>, to avoid duplicate keys.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">// A new file i18n/index.json</span></span>
<span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#8BE9FE">  "</span><span style="color:#8BE9FD">index.hello</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Hello</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// Another file i18n/common.json</span></span>
<span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#8BE9FE">  "</span><span style="color:#8BE9FD">common.hello</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Hello</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Each files can finally be imported and concatenated, thanks to destructing objects, to the list of translations in their related language layout (<code>layout/en.js</code> in this example).</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> React </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> Layout </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./layout</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> index </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">../../i18n/index.json</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> common </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">../../i18n/common.json</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@formatjs/intl-pluralrules/locale-data/en</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">props</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Layout</span></span>
<span class="line"><span style="color:#FF79C6">        {...</span><span style="color:#F8F8F2">props</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">        messages</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#FF79C6">            ...</span><span style="color:#F8F8F2">index,</span></span>
<span class="line"><span style="color:#FF79C6">            ...</span><span style="color:#F8F8F2">common</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#F8F8F2">    /></span></span>
<span class="line"><span style="color:#F8F8F2">);</span></span></code></pre><p>Using this method, we can split our translations in as much separate files as we would like, we “just” have to import and concatenate each new file we would create.</p>
<hr />
<h3>Placeholder, alt, aria-label and title</h3>
<p>In an <a href="https://medium.com/@Yuschick/translating-placeholder-alt-title-text-with-react-intl-a99d31f4194c" rel="noopener noreferrer external">article</a> of <a href="https://twitter.com/Yuschick" rel="noopener noreferrer external">Daniel Yuschick</a> I discovered that react-intl now exposes a <code>useIntl</code> hook which turns out to be kind handy to translate placeholder, alt, aria-label and title.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> React </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { useIntl } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">react-intl</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> Contact</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> intl </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useIntl</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">section</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">textarea</span><span style="color:#50FA7B;font-style:italic"> placeholder</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">intl.</span><span style="color:#50FA7B">formatMessage</span><span style="color:#F8F8F2">({ id</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">hello.world</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> })</span><span style="color:#FF79C6">}</span><span style="color:#50FA7B;font-style:italic"> name</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">message</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> rows</span><span style="color:#FF79C6">={</span><span style="color:#BD93F9">4</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">img</span><span style="color:#50FA7B;font-style:italic"> alt</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">intl.</span><span style="color:#50FA7B">formatMessage</span><span style="color:#F8F8F2">({ id</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">hello.yolo</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> })</span><span style="color:#FF79C6">}</span><span style="color:#50FA7B;font-style:italic"> src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">./yolo.png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#FF79C6">section</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><hr />
<h3>Links</h3>
<p>Translations sometimes contain HTML elements such as links. A sentence such a <code>The source code on &lt;a href=&quot;...&quot;&gt;GitHub&lt;/a&gt; is super.</code> would either need multiple entries for the translations or, a handy react-intl solution 😉.</p>
<p>The trick can be done by introducing a “placeholder” for the link in the translation (such as for example <code>{githubLink}</code> ).</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">source.code</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">The source code is available on {githubLink}.</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>When using the <code>FormattedMessage</code> component provided by react-intl, we can then parse, or replace, this “placeholder” with the effective link (HTML element).</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">FormattedMessage</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    id</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">source.code</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    values</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#F8F8F2">        githubLink</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FF79C6">a</span><span style="color:#50FA7B;font-style:italic"> href</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://github.com/deckgo/deckdeckgo</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>GitHub&#x3C;/</span><span style="color:#FF79C6">a</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    }</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#F8F8F2">/></span></span></code></pre><hr />
<h3>Overwrite Main Index.js</h3>
<p>Even though, as displayed in my previous article, I configured the website to route root path queries to the default language, I noticed that it is safe(r) to deploy the website with an <code>index.js</code> file.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">// ./gatsby-browser.js</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">exports</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">onClientEntry</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (window.location.pathname </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">/</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">        window.location.pathname </span><span style="color:#FF79C6">=</span><span style="color:#F1FA8C"> `/en`</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Because I don’t want to maintain multiple entry files manually, I created a pre-build script which copy <code>index.en.js</code> (my main index file) to <code>index.js</code> at build time.</p>
<p>In addition, the script also adds a comment at the start of the target file, so that I will remember that I should not edit it manually 😅.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">// copy-index.js</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> fs </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> require</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">fs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    fs.</span><span style="color:#50FA7B">copyFileSync</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">./src/pages/index.en.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">./src/pages/index.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> content </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> fs.</span><span style="color:#50FA7B">readFileSync</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">./src/pages/index.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> comment </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#E9F284">        "</span><span style="color:#F1FA8C">/**</span><span style="color:#FF79C6">\n</span><span style="color:#F1FA8C"> * Do not modify! This file is overwritten by index.en.js at build time.</span><span style="color:#FF79C6">\n</span><span style="color:#F1FA8C"> */</span><span style="color:#FF79C6">\n</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    fs.</span><span style="color:#50FA7B">writeFileSync</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">./src/pages/index.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, comment </span><span style="color:#FF79C6">+</span><span style="color:#F8F8F2"> content);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`index.en.js copied to index.js!`</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">} </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`Cannot copy index.en.js`</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>I set up this script by adding a <code>prebuild</code> target to my <code>package.json</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#E9F284">"</span><span style="color:#F1FA8C">scripts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">: {</span></span>
<span class="line"><span style="color:#8BE9FE">  "</span><span style="color:#8BE9FD">prebuild</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">node ./copy-index.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">  "</span><span style="color:#8BE9FD">develop</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">gatsby develop</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">  "</span><span style="color:#8BE9FD">start</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">gatsby develop</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">  "</span><span style="color:#8BE9FD">build</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">gatsby build</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">  "</span><span style="color:#8BE9FD">serve</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">gatsby serve</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">  "</span><span style="color:#8BE9FD">clean</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">gatsby clean</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">},</span></span></code></pre><hr />
<h3>Summary</h3>
<p>There might be some other easier solution to translate Gatsby website but, for having use my own recipe twice now, I am happy with the result and, the developer experience as well. That being said, I might search again for another solution next time, afterwards, never stop learning is what makes development fun.</p>
<p>Merry Christmas 🎄. To infinity and beyond!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Copy URL To Clipboard On Npm Run Start]]></title>
          <description><![CDATA[How to copy the start URL and parameters of your application to your clipboard while starting it locally.]]></description>
          <link>https://daviddalbusco.com/blog/copy-url-to-clipboard-on-npm-run-start/</link>
          <pubDate>Fri, 11 Dec 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*vO-uFCV6MmXmDCbHS5iO-Q.jpeg" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@giabyte?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Gia Oris</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<hr />
<p>I am not sure anyone will ever need the following tricks but, at my client’s place, we have an application which can only be accessed through an URL which contains a dynamic base64 parameter. Needless to say, it makes its local development a bit inconvenient.</p>
<p>In addition, I have to admit, I am not a big fan of CLI’s options which can automatically open browser. Even though I mostly use the Chrome Incognito mode for development purpose, I like to switch often between browsers to try out what I developed.</p>
<p>Finally, I don’t use bookmarks (don’t judge me) and, I often switch between technologies. Therefore, without configuration, every time another port has to be used ( <code>:3333</code> or <code>:4200</code> or <code>:8000</code> or <code>:8100</code> etc.).</p>
<p>That’s why I had the idea to add a pre-start script to the application which should copy the start URL of the application to my clipboard. Doing so, I can then just select a browser, paste the URI and, I am good to go.</p>
<hr />
<h3>Dependencies</h3>
<p>In order to copy or read value to the clipboard from a NodeJS script, I used the library <a href="https://github.com/sindresorhus/clipboardy" rel="noopener noreferrer external">clipboardy</a> which implement such a cross-platform feature.</p>
<p>In addition, I also added <a href="https://github.com/chalk/chalk" rel="noopener noreferrer external">chalk</a> ann <a href="https://github.com/sindresorhus/boxen" rel="noopener noreferrer external">boxen</a> to print out the URL to the console in a stylish way.</p>
<p>You may notice that all these dependencies are open source and developed by the same person, <a href="https://twitter.com/sindresorhus" rel="noopener noreferrer external">Sindre Sorhus</a>, which definitely deserve a shout-out for this awesome contribution and work 👍.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> i</span><span style="color:#F1FA8C"> clipboardy</span><span style="color:#F1FA8C"> chalk</span><span style="color:#F1FA8C"> boxen</span><span style="color:#BD93F9"> --save-dev</span></span></code></pre><hr />
<h3>Script</h3>
<p>Once the dependencies fetched, I created the pre-start script itself. In this example, I create a file <code>start-url.js</code> at the root of the project.</p>
<p>The script primary generates the <code>url</code> which I am looking to copy in my clipboard. On purpose, I am using an <code>Hello World 👋</code> string to demonstrate that it is possible to encode complex parameters.</p>
<p>To print out a nice message, I use <code>chalk</code> with colors, in for- and background, and the bold option. I use <code>\n\n</code> to create newlines. It is worth to notice that the library offer many options such as combining or nesting styles.</p>
<p>Using <code>clipboardy</code> to copy the URL to the clipboard is actually really few work and can be solved by calling its function <code>copy</code> with the all URL as input parameter.</p>
<p>Finally, in a <code>console.log</code> , I print out the message inside a box generated with <code>boxen</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> { write</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> copy } </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> require</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">clipboardy</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> chalk </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> require</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">chalk</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> boxen </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> require</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">boxen</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> params </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> encodeURIComponent</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Hello World 👋</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> url </span><span style="color:#FF79C6">=</span><span style="color:#F1FA8C"> `http://localhost:3333/profile/</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">params</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> message </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> chalk.yellow.</span><span style="color:#50FA7B">inverse</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Your URL</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    message </span><span style="color:#FF79C6">+=</span><span style="color:#F1FA8C"> `</span><span style="color:#FF79C6">\n\n${</span><span style="color:#F8F8F2">chalk.</span><span style="color:#50FA7B">bold</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">url</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        await</span><span style="color:#50FA7B"> copy</span><span style="color:#F8F8F2">(url);</span></span>
<span class="line"><span style="color:#F8F8F2">        message </span><span style="color:#FF79C6">+=</span><span style="color:#F1FA8C"> `</span><span style="color:#FF79C6">\n\n${</span><span style="color:#F8F8F2">chalk.</span><span style="color:#50FA7B">grey</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Copied local address to clipboard!</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#F8F8F2">        message </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> chalk.red.</span><span style="color:#50FA7B">bold</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`Cannot copy </span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">url</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C"> to clipboard 🥺</span><span style="color:#FF79C6">\n\n${</span><span style="color:#F8F8F2">err.message</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#50FA7B">        boxen</span><span style="color:#F8F8F2">(message, {</span></span>
<span class="line"><span style="color:#F8F8F2">            borderStyle</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">round</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">            padding</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 1</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">            borderColor</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">yellow</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">            margin</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 1</span></span>
<span class="line"><span style="color:#F8F8F2">        })</span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">})();</span></span></code></pre><hr />
<h3>Life Cycle</h3>
<p>Thanks to <a href="https://docs.npmjs.com/cli/v6/using-npm/scripts" rel="noopener noreferrer external">npm</a> it is possible to execute scripts at different life cycles. Because I was looking to copy the URL when I start locally the application, I added it as a <code>prestart</code> script to my <code>package.json</code>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#E9F284">"</span><span style="color:#F1FA8C">scripts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">: {</span></span>
<span class="line"><span style="color:#8BE9FE">  "</span><span style="color:#8BE9FD">prestart</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">node start-url.js</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>You might ask “But David, why a prestart and not poststart?” to which I would answer that <code>prestart</code> is executed for sure. On the contrary, <code>poststart</code>, is indeed also executed but, only when the <code>start</code> life cycle would be released. If your local server, as often, would stay active to watch for changes, <code>poststart</code> would only resolve when you would cancel your local server.</p>
<hr />
<h3>Demo</h3>
<p>Everything being in place, when I hit <code>npm run start</code> in my console, the URL is generated, copied to the clipboard and, I notice the following output.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*ld-QYTtC5iBSisGVvNzPMA.png" alt="" / loading="lazy"></p>
<p>If I open one of my browser, I can paste the result in the navigation bar.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*glgIo-xgThAKIlL-Y1uqtw.png" alt="" / loading="lazy"></p>
<p>Finally, hit <code>enter</code> and access my URL with the generated parameters.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*Zgx91lAb1eO8wN66IqYuYA.png" alt="" / loading="lazy"></p>
<hr />
<h3>Summary</h3>
<p>As underlined in the introduction of this post, I am really not sure if anyone will ever need this solution ever 😅. Anyway, I hope it was a fun read and let me know if you would improve anything.</p>
<p>To infinity and beyond!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Angular: Create A Lazy Loaded Tailwind Modal]]></title>
          <description><![CDATA[Build a generic, lazy loaded, dialog with Angular and Tailwind CSS.]]></description>
          <link>https://daviddalbusco.com/blog/angular-create-a-lazy-loaded-tailwind-modal/</link>
          <pubDate>Mon, 30 Nov 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*5yG0_AlxnZfzLYHwbwrcDQ.jpeg" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@emilegt?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Emile Guillemot</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<hr />
<p>I have the opportunity to participate to <a href="https://owlly.ch/" rel="noopener noreferrer external">Owlly</a>, an amazing and meaningful <a href="https://github.com/project-owlly" rel="noopener noreferrer external">open source</a> project, founded by <a href="https://twitter.com/saandr0o" rel="noopener noreferrer external">Sandro Scalco</a>, which aims to enable digital democracy in Switzerland🇨🇭.</p>
<p>Last week, as we were discussing the need to pre-render the main Angular application using <a href="https://scully.io" rel="noopener noreferrer external">Scully</a>, we also took the decision to migrate it to <a href="https://tailwindcss.com/" rel="noopener noreferrer external">Tailwind CSS</a>.</p>
<p>As a result, I notably had to create a custom generic lazy loaded modal.</p>
<hr />
<h3>Meta</h3>
<p>This blog post has been published in November 2020. The solution has been tested with Angular v11 and Tailwind v2.</p>
<hr />
<h3>Introduction</h3>
<p>This tutorial describes the creation of a generic dialog with Angular and Tailwind CSS. With generic, I mean that the goal is the creation of a dialog's container which can be reused several times in the application, with different content, without the need to rewrite everything multiple times.</p>
<p>In addition, it was and is also important to me that the modal content is lazy loaded for the best performances.</p>
<hr />
<h3>Add Tailwind CSS</h3>
<p>I have tried various solutions to add Tailwind CSS to Angular application and despite a small <a href="https://github.com/ngneat/tailwind/issues/16" rel="noopener noreferrer external">issue</a>, which is probably going to be solved soon, the <a href="https://github.com/ngneat/tailwind" rel="noopener noreferrer external">Tailwind schematic</a> provided by the team ngneat is by far the simplest method I tried out.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">ng</span><span style="color:#F1FA8C"> add</span><span style="color:#F1FA8C"> @ngneat/tailwind</span></span></code></pre><p>Run the above command, follow the prompt and enjoy.</p>
<hr />
<h3>Service</h3>
<p>In order to <code>open</code> and <code>close</code> any modals, we create a service <code>modal.service.ts</code>. It takes care of these operations and, it also takes care of attaching them to the DOM <code>body</code>.</p>
<p>Regarding this operation, to be honest with you, I did not know spontaneously how such things can be coded in Angular and, I had to google for a solution. Fortunately, I found this nice <a href="https://medium.com/hackernoon/angular-pro-tip-how-to-dynamically-create-components-in-body-ba200cc289e6" rel="noopener noreferrer external">article</a> of Carlos Roso which describes the required steps.</p>
<p>Finally, as the service is provided in <code>root</code>, it is worth to notice that we keep in memory the reference to the component which is currently attached, respectively displayed. Doing so, we are allowing only one modal at a time. If would have the requirement to display multiple elements at the same time, I would suggest you to handle these with an array instead of a single class variable.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  ApplicationRef,</span></span>
<span class="line"><span style="color:#F8F8F2">  ComponentFactoryResolver,</span></span>
<span class="line"><span style="color:#F8F8F2">  ComponentRef,</span></span>
<span class="line"><span style="color:#F8F8F2">  EmbeddedViewRef,</span></span>
<span class="line"><span style="color:#F8F8F2">  Injectable,</span></span>
<span class="line"><span style="color:#F8F8F2">  Injector,</span></span>
<span class="line"><span style="color:#F8F8F2">  Type,</span></span>
<span class="line"><span style="color:#F8F8F2">} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Injectable</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  providedIn</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">root</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> ModalService</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#F8F8F2"> componentRef</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> ComponentRef</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">|</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  constructor</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#FF79C6">    private</span><span style="color:#FFB86C;font-style:italic"> componentFactoryResolver</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> ComponentFactoryResolver</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#FF79C6">    private</span><span style="color:#FFB86C;font-style:italic"> appRef</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> ApplicationRef</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#FF79C6">    private</span><span style="color:#FFB86C;font-style:italic"> injector</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Injector</span></span>
<span class="line"><span style="color:#F8F8F2">  ) {}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  async</span><span style="color:#50FA7B"> open</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">component</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Type</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">>)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">void</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.componentRef) {</span></span>
<span class="line"><span style="color:#FF79C6">      return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.componentRef </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.componentFactoryResolver</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">resolveComponentFactory</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">>(component)</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">create</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.injector);</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.appRef.</span><span style="color:#50FA7B">attachView</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.componentRef.hostView);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> domElem </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> (</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.componentRef.hostView </span><span style="color:#FF79C6">as</span></span>
<span class="line"><span style="color:#F8F8F2">                     EmbeddedViewRef</span><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2">any</span><span style="color:#FF79C6">></span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">                     .rootNodes[</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">] </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> HTMLElement</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    document.body.</span><span style="color:#50FA7B">appendChild</span><span style="color:#F8F8F2">(domElem);</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  async</span><span style="color:#50FA7B"> close</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">void</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.componentRef) {</span></span>
<span class="line"><span style="color:#FF79C6">      return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.appRef.</span><span style="color:#50FA7B">detachView</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.componentRef.hostView);</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.componentRef.</span><span style="color:#50FA7B">destroy</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.componentRef </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>Modal Container</h3>
<p>To initialize the modal, the container, we create a new module <code>modal.module.ts</code>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { NgModule } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { CommonModule } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@angular/common</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { ModalComponent } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./modal.component</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">NgModule</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    declarations</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#50FA7B;font-style:italic">ModalComponent</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">    imports</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#50FA7B;font-style:italic">CommonModule</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">    exports</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#50FA7B;font-style:italic">ModalComponent</span><span style="color:#F8F8F2">]</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> ModalModule</span><span style="color:#F8F8F2"> {}</span></span></code></pre><p>We then add the related component <code>modal.component.ts</code> which does not do much except being created with a state <code>display</code> per default initialized to <code>true</code> and exposes a function <code>close</code>.</p>
<p>As we are lazy loading the modals, these are going to be displayed upon creation, therefore the default state is <code>open</code> respectively not closed.</p>
<p>The close function contains a small <code>timeout</code> so that the modal first graphically fade away before being effectively detached from the DOM by the service we just created previously.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Component} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {ModalService} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">../../services/modal.service</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  selector</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-modal</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  templateUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./modal.component.html</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  styleUrls</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">./modal.component.scss</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> ModalComponent</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#F8F8F2">  display </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  constructor</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">private</span><span style="color:#FFB86C;font-style:italic"> modalService</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> ModalService</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">>) {}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  async</span><span style="color:#50FA7B"> close</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">void</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.display </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    setTimeout</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">      await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.modalService.</span><span style="color:#50FA7B">close</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    }, </span><span style="color:#BD93F9">300</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>The HTML code of the container is extracted from the <a href="https://tailwindui.com/components/application-ui/overlays/modals" rel="noopener noreferrer external">free overlay example</a> provided by Tailwind. We are using a <code>section</code> for which we apply a <code>fixed</code> position and to which we give a <code>z-index</code> of <code>10</code> . In addition, we are responsively styling the required spaces, shadows and sizes.</p>
<p>Beside the UI itself, it is worth to notice that we are using the Angular content projection capability, <code>ng-content</code>, to be able to add any content in the modal respectively to makes this dialog a generic container.</p>
<p>We also attach the <code>close</code> function to the section and, we stop the propagation of the <code>$event</code> on its content, otherwise the modal would close itself each time one of its children would be clicked or pressed.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">section</span></span>
<span class="line"><span>  </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">[class.open]="display"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">  class</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">fixed z-10 inset-0 overflow-y-auto</span><span style="color:#E9F284">"</span></span>
<span class="line"><span>  </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">(click)="close()"</span></span>
<span class="line"><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;</span><span style="color:#FF79C6">div</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    class</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:p-0 bg-gray-900 bg-opacity-40</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">  ></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">div</span></span>
<span class="line"><span>      </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">(click)="$event.stopPropagation()"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">      class</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">      role</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">dialog</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">      aria-modal</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">true</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">      aria-labelledby</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">modal-headline</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    ></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> class</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ng-content</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ng-content</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">section</span><span style="color:#F8F8F2">></span></span></code></pre><p>Finally, we animate the opening and closing of the modal upon the style class <code>open</code> with some custom CSS. It might be possible to achieve this with some Tailwind utilities but, I felt more confident to solved it that way.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">section</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD">    visibility</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> hidden</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">    opacity</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    &#x26;</span><span style="color:#50FA7B;font-style:italic">.open</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD">        visibility</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> inherit</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">        opacity</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 1</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">    transition</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD"> opacity</span><span style="color:#BD93F9"> 250</span><span style="color:#FF79C6">ms</span><span style="color:#BD93F9"> ease-in</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>Modal Example</h3>
<p>The above service and container being set, we are now able to use these to create any modals. As for example the following one in which the user would be able to input a username.</p>
<p>Note that the example contains a form but, it is not mandatory. To the contrary and really <strong>important</strong> to notice:</p>
<p>I advise you to <strong>DO NOT</strong> create a separate file for the module declaration but, in this specific case, to declare its module within the same file as the component.</p>
<p>You might not face the same error as I did but, as we are using a bunch a core components declared and referenced in another separate module, Angular was complaining at build time it was unable to resolve these until I finally figured out that adding the module within the component file would solve the build issue.</p>
<p>Beside this, your component being projected in the modal container, it basically works as any other standalone component.</p>
<p>In case you would like to add a button to close the modal from its content or close it following the completion of a function, you can, as displayed in the example, use a <code>ViewChild</code> to access the container and call the <code>close</code> method we declared previously.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Component, NgModule, ViewChild} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  FormGroup,</span></span>
<span class="line"><span style="color:#F8F8F2">  FormBuilder,</span></span>
<span class="line"><span style="color:#F8F8F2">  Validators,</span></span>
<span class="line"><span style="color:#F8F8F2">  FormsModule,</span></span>
<span class="line"><span style="color:#F8F8F2">  ReactiveFormsModule,</span></span>
<span class="line"><span style="color:#F8F8F2">} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/forms</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {CommonModule} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/common</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {ModalModule} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">..//modal/modal.module</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {ModalComponent} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">../modal/modal.component</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  selector</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-newsletter</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  templateUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./newsletter.component.html</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  styleUrls</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">./newsletter.component.scss</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> NewsletterComponent</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  @</span><span style="color:#50FA7B">ViewChild</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">modalComponent</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">) modal</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#FF79C6">    |</span><span style="color:#8BE9FD;font-style:italic"> ModalComponent</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">NewsletterComponent</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#FF79C6">    |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  newsletterForm</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> FormGroup</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  constructor</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#FF79C6">    public</span><span style="color:#FFB86C;font-style:italic"> fb</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> FormBuilder</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  ) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.newsletterForm </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.fb.</span><span style="color:#50FA7B">group</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">      username</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">''</span><span style="color:#F8F8F2">, [Validators.required]]</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  async</span><span style="color:#50FA7B"> createRecord</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">void</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.newsletterForm.value);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">close</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  async</span><span style="color:#50FA7B"> close</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">void</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.modal?.</span><span style="color:#50FA7B">close</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">NgModule</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  imports</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    CommonModule</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    FormsModule</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    ReactiveFormsModule</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    ModalModule</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  ],</span></span>
<span class="line"><span style="color:#F8F8F2">  declarations</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#50FA7B;font-style:italic">NewsletterComponent</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> NewsletterComponentModule</span><span style="color:#F8F8F2"> {}</span></span></code></pre><p>The key of the template is the encapsulation of the content in the container, in the <code>app-modal</code> component we have created previously. Beside, as for the code of the component, nothing particular to notice.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">app-modal</span><span style="color:#50FA7B;font-style:italic"> #modalComponent</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">form</span><span style="color:#50FA7B;font-style:italic"> [formGroup]</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">newsletterForm</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> (ngSubmit)</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">createRecord()</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">label</span><span style="color:#50FA7B;font-style:italic"> for</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">username</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> class</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">block mt-2 text-xs font-semibold text-gray-600</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">            >Username &#x3C;</span><span style="color:#FF79C6">span</span><span style="color:#50FA7B;font-style:italic"> class</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">text-red-600</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>*&#x3C;/</span><span style="color:#FF79C6">span</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">label</span></span>
<span class="line"><span style="color:#F8F8F2">        ></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">input</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            id</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">username</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            type</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">text</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            name</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">firstname</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            formControlName</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">username</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            class</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">block w-full p-3 mt-2 text-gray-700 bg-gray-200 appearance-none focus:outline-none focus:bg-gray-300 focus:shadow-inner</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            required</span></span>
<span class="line"><span style="color:#F8F8F2">        /></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">ng-container</span><span style="color:#50FA7B;font-style:italic"> *ngTemplateOutlet</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">toolbar</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">ng-container</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">form</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">app-modal</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">ng-template</span><span style="color:#50FA7B;font-style:italic"> #toolbar</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> class</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">py-3 flex justify-end</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">button</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            (click)</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">close()</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            type</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">button</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            class</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">rounded-md shadow-lg sm:tracking-wider mx-2 border border-gray-300 px-4 py-2 bg-white text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">        ></span></span>
<span class="line"><span style="color:#F8F8F2">            Close</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#FF79C6">button</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">button</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            type</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">submit</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            class</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">bg-yellow-300 hover:bg-yellow-400 text-black font-bold rounded-md shadow-lg sm:tracking-wider py-2 px-4</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            [disabled]</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">newsletterForm.invalid</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">        ></span></span>
<span class="line"><span style="color:#F8F8F2">            Submit</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#FF79C6">button</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">ng-template</span><span style="color:#F8F8F2">></span></span></code></pre><hr />
<h3>Modal Creation</h3>
<p>Finally, thanks to dynamic import, we load our example of modal on demand and therefore fetch its related code only when needed. Moreover, we are using our service to <code>open</code> it and attach it to the <code>body</code> of the DOM.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Component} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {ModalService} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./modal.service</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {</span><span style="color:#FFB86C;font-style:italic">NewsletterComponent</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> NewsletterComponentType} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./newsletter/newsletter.component</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  selector</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-landing</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  template</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> `</span></span>
<span class="line"><span style="color:#F1FA8C">    &#x3C;button</span></span>
<span class="line"><span style="color:#F1FA8C">      type="button"</span></span>
<span class="line"><span style="color:#F1FA8C">      (click)="showNewsletter()"</span></span>
<span class="line"><span style="color:#F1FA8C">      class="bg-yellow-300 hover:bg-yellow-400 text-black font-bold rounded-md shadow-lg sm:tracking-wider py-2 px-4 m-8"</span></span>
<span class="line"><span style="color:#F1FA8C">    >Newsletter&#x3C;/button</span></span>
<span class="line"><span style="color:#F1FA8C">    ></span></span>
<span class="line"><span style="color:#F1FA8C">  `</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> LandingComponent</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  constructor</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">private</span><span style="color:#FFB86C;font-style:italic"> modalService</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> ModalService</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">NewsletterComponentType</span><span style="color:#F8F8F2">>) {}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  async</span><span style="color:#50FA7B"> showNewsletter</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">void</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> {NewsletterComponent} </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> import</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#E9F284">      '</span><span style="color:#F1FA8C">./newsletter/newsletter.component</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.modalService.</span><span style="color:#50FA7B">open</span><span style="color:#F8F8F2">(NewsletterComponent);</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>If everything work as expected, the modal should be lazy loaded and, we should be able to open and close the example modal.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*BY_Wt6hf0noQbjakv_2I5A.gif" alt="" / loading="lazy"></p>
<hr />
<h3>Epilogue</h3>
<p>I am really grateful to have had the opportunity to be hired as a freelancer to collaborate on a project like <a href="https://owlly.ch/" rel="noopener noreferrer external">Owlly</a>. Once again, thank you Sandro for the opportunity. I also hope this tutorial is going to be helpful to anyone looking to set up modals with Angular and Tailwind and if you have idea of improvements, let me know!</p>
<p>To infinity and beyond!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[StencilJS & Web Worker: A Fairy Tale]]></title>
          <description><![CDATA[The story of the Stencil's compiler and its magical integration of the Web Worker API.]]></description>
          <link>https://daviddalbusco.com/blog/stenciljs-and-web-worker-a-fairy-tale/</link>
          <pubDate>Thu, 19 Nov 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*WXwuHYRWLLamBbB5zg6gtg.png" alt="" / loading="lazy"></p>
<hr />
<p>Once upon a time, there was a compiler that generates Web Components and builds high performance web apps called <a href="https://stenciljs.com" rel="noopener noreferrer external">StencilJS</a>. Among all the build-time tool ever created, it had for goal to build faster, more capable components that worked across all major frameworks.</p>
<p>On the internet next door, there lived a boy (me 😉). And the boy watched the compiler grow more and more effective, more and more developer friendly with each passing year.</p>
<p>One day, as he was developing something new in his beloved project <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>, the boy had the idea to experiment a feature of the compiler he had never tried so far, the integration of <a href="https://stenciljs.com/docs/web-workers" rel="noopener noreferrer external">Web Workers</a>.</p>
<p>He was so blew away by the results, that he had to share that magical encounter.</p>
<hr />
<h3>Chapter One: Abracadabra</h3>
<p>A boy publishes a blog post but would not reveal any unknown secret spells. Meanwhile, there would be no good fairy tale without magic words.</p>
<p>Fortunately, the compiler has shared its sorcery to anyone publicly in a very well documented grimoire called “<a href="https://stenciljs.com/docs/web-workers" rel="noopener noreferrer external">documentation</a>”.</p>
<p>Valiant knights seeking to technically defeat such implementation, let me suggest you to have a look to these spells but, if to the contrary you are here to find a quest, stay with me and let me tell you the rest of the story.</p>
<hr />
<h3>Chapter Two: Init Camelot</h3>
<p>King Arthur and the Round Table had Camelot, we, developer, have Web Components and shadow DOM, which can be metaphorically represented as castles. That’s why we are initializing a new Stencil playground before experimenting new magical tricks.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> init</span><span style="color:#F1FA8C"> stencil</span></span></code></pre><p>In addition, to replicate the exact formula the boy had tried out, we enhance our fortifications with the usage of <a href="https://marked.js.org/" rel="noopener noreferrer external">Marked.js</a> so that we give our component the goal to render a magical sentence from Markdown to HTML.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">npm i marked @</span><span style="color:#50FA7B;font-style:italic">types</span><span style="color:#FF79C6">/</span><span style="color:#50FA7B;font-style:italic">marked</span></span></code></pre><p>Having found some mortar, we are creating a component which aims to reproduce the rendering spell by transforming markdown to HTML when the lifecycle <code>componentWillLoad</code> and applying it through the use of a local state.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Component, h, State } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@stencil/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { parseMarkdown } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./markdow</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  tag</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">my-camelot</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  shadow</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> MyCamelot</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  @</span><span style="color:#50FA7B">State</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#F8F8F2"> markdownHtml</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  async</span><span style="color:#50FA7B"> componentWillLoad</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.markdownHtml </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> parseMarkdown</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`# Choose wisely</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F1FA8C">For while the true Grail will **bring you life**, the false Grail will take it from you.`</span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> innerHTML</span><span style="color:#FF79C6">={</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.markdownHtml</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>In addition, we externalize the magical function in a separate file we can call <code>markdown.ts</code>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> marked </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">marked</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> parseMarkdown</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">text</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> renderer </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#F8F8F2"> marked.</span><span style="color:#50FA7B">Renderer</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#50FA7B"> marked</span><span style="color:#F8F8F2">(text, {</span></span>
<span class="line"><span style="color:#F8F8F2">    renderer,</span></span>
<span class="line"><span style="color:#F8F8F2">    xhtml</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Some who might fear nothing and who might run the above code on their local computers, <code>npm run start</code>, might observe the following outcome.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*ube4TfnIPeJrXX0vD0wRDA.png" alt="" / loading="lazy"></p>
<hr />
<h3>Chapter Three: Spell Calling</h3>
<p>The boy had already been published articles on Web Workers, one about their native <a href="https://levelup.gitconnected.com/react-and-web-workers-c9b60b4b6ae8" rel="noopener noreferrer external">JavaScript integration in React</a> applications and another one showcasing their <a href="https://medium.com/swlh/angular-and-web-workers-17cd3bf9acca" rel="noopener noreferrer external">integration with Angular</a>.</p>
<p>From taking care of making libraries available for the workers, in the Javascript version, to using listeners and creating objects to load these in both of these, even if from a certain perspective it needed few works, it still needed more work, more code.</p>
<p>To the contrary and to the boy wonder, Stencil made all these steps magical by simply calling a unique spell: <code>mv markdown.ts markdown.worker.ts</code></p>
<p>Indeed, as you can notice in following screenshot, any TypeScript file within the <code>src</code> directory that ends with <code>.worker.ts</code> will automatically use a worker by the Stencil’s compiler making, as far as the boy knows, the most magical Web Worker recipe he ever tried out 🔥.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*WjLbi1ajmi4H0rlACo4dPA.png" alt="" / loading="lazy"></p>
<hr />
<h3>Epilogue</h3>
<p>The <a href="https://stenciljs.com" rel="noopener noreferrer external">Stencil’s</a> compiler, having simplified this kind of integration, demonstrated once again all its potential. Together with the Web Worker, they hopefully will have many babies, many amazing Web Components and applications.</p>
<p>To infinity and beyond!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[App Store & Google Play: Submission Checklist]]></title>
          <description><![CDATA[A bare minimum checklist of the information required to submit your application to the App Store and Google Play.]]></description>
          <link>https://daviddalbusco.com/blog/app-store-and-google-play-submission-checklist/</link>
          <pubDate>Tue, 03 Nov 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*uLq44jxngEeseb70ImZZDA.png" alt="" / loading="lazy"></p>
<p><em>Background’s photo by <a href="https://unsplash.com/@jessbaileydesigns?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Jess Bailey</a> on <a href="https://unsplash.com/s/photos/background?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>One of my current client requested a list of the <strong>bare minimum</strong> information required to publish their app in the App Store and Google Play. Once again, I did not remember where I saved such check-list 😅.</p>
<p>That’s why, instead of charging them for such process, I had the idea to wrap up these information in form of a public blog post I’ll try, hopefully with your help and inputs, to maintain up-to-date.</p>
<hr />
<h3>Last Update</h3>
<p>This blog post has been updated for the last time <strong>Tuesday 3rd November 2020</strong>.</p>
<hr />
<h3>Distribution</h3>
<p>Following questions have to be answered in order to distribute the app in both <strong>App Store</strong> and <strong>Google Play</strong>:</p>
<p><span>1.</span> Is the app available everywhere (in the world) for download or should it only available in particular countries?</p>
<p>If for example you restrain the distribution to a country such as “Switzerland”, a potential user who registered his/her account in another App Store located country, as for example “Germany”, would not be able to find and download the application.</p>
<p><span>2.</span> Is it for free or what’s its selling price?</p>
<p><span>3.</span> Does the app contain ads?</p>
<p><span>4.</span> Which category is the most accurate and effective for the app?</p>
<p>You can find a list and introduction to the categories via these links:</p>
<ul>
<li>App Store: <a href="https://developer.apple.com/app-store/categories/" rel="noopener noreferrer external">https://developer.apple.com/app-store/categories/</a></li>
<li>Google Play: <a href="https://support.google.com/googleplay/android-developer/answer/113475?hl=en" rel="noopener noreferrer external">https://support.google.com/googleplay/android-developer/answer/113475?hl=en</a></li>
</ul>
<p>In addition, <strong>Google Play</strong> (only) request following information about the app’s content:</p>
<p><span>5.</span> Content ratings: the application’s has to receive content ratings from official rating authorities.</p>
<p>For example: PEGI-3. To get these, a step by step wizard is built-in the Google Play console.</p>
<p><span>6.</span> Target audience: the target age group of the app</p>
<hr />
<h3>External Links</h3>
<p>Both stores require links, external URLs, to information such as data privacy or terms of use.</p>
<p><strong>App Store:</strong></p>
<ul>
<li>Data privacy</li>
<li>Support: a link to a page where your potential users can request help</li>
<li>Marketing: a link to a page about commercial information regarding your application</li>
<li>Copyright: name or entity which owns the legal right</li>
</ul>
<p><strong>Google Play:</strong></p>
<ul>
<li>Data privacy</li>
<li>Terms of use: I am actually not sure anymore it is required. I’ll check next time I submit a new application.</li>
<li>E-mail: a contact email which is going to be visible in store too</li>
</ul>
<hr />
<h3>Information</h3>
<p>To introduce and showcase your application in store, you need to provide different information such as title, description etc. These have to be provided and have to match specific maximum lengths (including spaces).</p>
<p><strong>App Store:</strong></p>
<ul>
<li>Name: max. 30 characters</li>
<li>Subtitle: max. 30 characters</li>
<li>Promotional Text: max. 170 characters</li>
</ul>
<p>Apple recommends using it to catch the eye of new potential users. It is worth to notice that it can be updated without having to submit a new build.</p>
<ul>
<li>Description: max. 4000 characters</li>
<li>Keywords: max. 100 characters (including comma). a comma separated list of keywords.</li>
</ul>
<p><strong>Google Play:</strong></p>
<ul>
<li>Name: max. 50 characters</li>
<li>Short description: max. 80 characters</li>
<li>Description: max. 4000 characters</li>
</ul>
<hr />
<h3>Screenshots</h3>
<p>Finally, these are the requirements to provide stunning screenshots of your app.</p>
<p><strong>App Store: iPhone</strong></p>
<p><span>1.</span> 6.5 inch (iPhone 11 Pro Max, iPhone 11, iPhone XSMax, iPhone XR)</p>
<ul>
<li>Minimum 3 screenshots up to 10</li>
<li>1242 x 2688 pixels (portrait) or 2688 x 1242 pixels (landscape)</li>
</ul>
<p><span>2.</span> 5.5 inch (iPhone 8 Plus, iPhone 7 Plus, iPhone 6s Plus)</p>
<ul>
<li>Minimum 3 screenshots up to 10</li>
<li>1242 x 2208 pixels (portrait) or 2208 x 1242 pixels (landscape)</li>
</ul>
<p><strong>App Store: iPad</strong></p>
<p><span>1.</span> 12.9 inch (iPad Pro (4th generation, 3rd generation))</p>
<ul>
<li>Minimum 3 screenshots up to 10</li>
<li>2048 x 2732 pixels (portrait) or 2732 x 2048 pixels (landscape)</li>
</ul>
<p><span>2.</span> 12.9 inch (2nd generation iPad Pro)</p>
<ul>
<li>Minimum 3 screenshots up to 10</li>
<li>2048 x 2732 pixels (portrait) or 2732 x 2048 pixels (landscape)</li>
</ul>
<p>As you can notice, both iPads required Screenshots’ size are the same, just in case 😉.</p>
<p><strong>Google Play:</strong></p>
<p><span>1.</span> Icon</p>
<ul>
<li>JPEG or 32-bit PNG</li>
<li>512 px by 512 px</li>
<li>Up to 1 MB</li>
</ul>
<p><span>2.</span> Feature graphic: A banner to help promote your app in different places on Google Play</p>
<ul>
<li>JPEG or 24-bit PNG (not transparent)</li>
<li>1024 x 500 pixels</li>
<li>Up to 1 MB</li>
</ul>
<p><span>3.</span> Phone screenshots</p>
<ul>
<li>Minimum 2 screenshots up to 8</li>
<li>JPEG or 24-bit PNG (not transparent)</li>
<li>Between 320 pixels and 3840 pixels</li>
<li>16:9 aspect ratio (for landscape screenshots)</li>
<li>Up to 8 MB</li>
</ul>
<p><span>4.</span> 7-inch tablet screenshots</p>
<ul>
<li>Up to 8 screenshots</li>
<li>JPEG or 24-bit PNG (not transparent)</li>
<li>Between 320 pixels and 3840 pixels</li>
<li>16:9 aspect ratio (for landscape screenshots)</li>
<li>Up to 8 MB</li>
</ul>
<p><span>5.</span> 10-inch tablet screenshots</p>
<ul>
<li>Up to 8 screenshots</li>
<li>JPEG or 24-bit PNG (not transparent)</li>
<li>Between 320 pixels and 3840 pixels</li>
<li>16:9 aspect ratio (for landscape screenshots)</li>
<li>Up to 8 MB</li>
</ul>
<p>Note that both above tablet screenshots are optional.</p>
<hr />
<h3>Conclusion</h3>
<p>Both stores offer more options, such as videos or variety of formats, but the goal of this blog post focused only on bare requirements.</p>
<p>I hope it will be useful to you too and if you notice anything missing or which changed since its publication, please let me know.</p>
<p>To infinity and beyond!</p>
<p>David</p>
<hr />
<p>Reach me out on <a href="https://twitter.com/daviddalbusco" rel="noopener noreferrer external">Twitter</a> and, why not, give a try to <a href="https://deckdeckgo.com/" rel="noopener noreferrer external">DeckDeckGo</a> for your next presentations.</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Recursively Clean Firestore FieldValue.delete()]]></title>
          <description><![CDATA[How to recursively remove delete methods from the document's object you just updated and have in memory.]]></description>
          <link>https://daviddalbusco.com/blog/recursively-clean-firestore-fieldvalue-delete/</link>
          <pubDate>Thu, 15 Oct 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*2lFmIJDlVr6D_q8iAchhxw.jpeg" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@thecreative_exchange?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">The Creative Exchange</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>This morning I had to improve a function we used in <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a> to recursively clean objects after persistence. Because I am currently quite busy but would not like to push my blogging habits too much on the side, I got the idea that this small &quot;hack&quot; would be a nice subject for a new blog post 🤗.</p>
<hr />
<h3>Introduction</h3>
<p>When you use <a href="https://firebase.google.com/docs/firestore/" rel="noopener noreferrer external">Cloud Firestore</a>, in order to delete specific fields from a document, you have to use the <code>FieldValue.delete()</code> method when you update a document (as displayed in the <a href="https://firebase.google.com/docs/firestore/manage-data/delete-data" rel="noopener noreferrer external">documentation</a>).</p>
<p>For example, if your database contains a document such as the following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">description</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Hello World</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>You have to use the above method to remove it because setting it for example to <code>null</code> would not remove the attribute but “only” set its value to <code>null</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#BD93F9"> *</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> firebase </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">firebase/app</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">firebase/firestore</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> firestore </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> firebase.</span><span style="color:#50FA7B">firestore</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> ref </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> firestore.</span><span style="color:#50FA7B">collection</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">users</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">).</span><span style="color:#50FA7B">doc</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">david</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> user </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    description</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> firebase.firestore.FieldValue.</span><span style="color:#50FA7B">delete</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">await</span><span style="color:#F8F8F2"> ref.</span><span style="color:#50FA7B">update</span><span style="color:#F8F8F2">(user);</span></span></code></pre><p>Thanks to this method, the above document's example becomes <code>{}</code> in the database.</p>
<hr />
<h3>Issue</h3>
<p>This method works like a charm but can lead to an issue. Indeed, if you are not refreshing your local object after its update, it will still contain the method <code>FieldValue.delete()</code> afterwards, which does not reflect its effective value in database.</p>
<p>Concretely, with our above example, if we would print out the <code>user</code> to the console, its output would be the following.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span>  </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">description</span><span style="color:#FF79C6">:</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">n</span><span style="color:#F8F8F2"> {</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">h_</span><span style="color:#FF79C6">:</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">n</span><span style="color:#F8F8F2">}</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>This can lead to some unexpected behavior in your application, if you are still using the object after its update, notably if it is a state.</p>
<hr />
<p>To overcome this issue, one solution would be explicitly fetch the newly updated document from Firestore, what is also happening automatically if you have developed some polling to fetch the information or if you are using libraries such as <a href="https://github.com/angular/angularfire" rel="noopener noreferrer external">AngularFire</a> or <a href="https://github.com/firebase/firebase-js-sdk/tree/master/packages/rxfire" rel="noopener noreferrer external">RxFire</a>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#BD93F9"> *</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> firebase </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">firebase/app</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">firebase/firestore</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> firestore </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> firebase.</span><span style="color:#50FA7B">firestore</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> ref </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> firestore.</span><span style="color:#50FA7B">collection</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">users</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">).</span><span style="color:#50FA7B">doc</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">david</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">let</span><span style="color:#F8F8F2"> user </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    description</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> firebase.firestore.FieldValue.</span><span style="color:#50FA7B">delete</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">await</span><span style="color:#F8F8F2"> ref.</span><span style="color:#50FA7B">update</span><span style="color:#F8F8F2">(user);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">user </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> ref.</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(user); </span><span style="color:#6272A4">// {}</span></span></code></pre><p>This solution has the advantage to keep your objects in sync with the database but has the disadvantage to cost you an extra query.</p>
<p>Indeed, when you use <a href="https://firebase.google.com/docs/firestore/" rel="noopener noreferrer external">Cloud Firestore</a>, you are <a href="https://cloud.google.com/firestore/pricing" rel="noopener noreferrer external">charged</a> according the number of reads, writes, and deletes that you perform. Therefore, one more query can, according its frequency, leads to more costs.</p>
<p>That’s why I came up to the idea of cleaning recursively clean the method <code>FieldValue.delete()</code>, why I had the idea of a &quot;hack&quot; 😎.</p>
<hr />
<h3>Solution</h3>
<p>The following function <code>filterDelete</code> iterates all <code>keys</code> of an object and identify these which have to be ignored (<code>shouldAttributeBeCleaned</code> ), these which contains the method <code>FieldValue.delete()</code>.</p>
<p>If not ignored, then it recursively calls the function <code>filterDelete</code> for the current child und this until all children have been processed the same way.</p>
<p>In addition, as the reducer is initialized with an empty object <code>{}</code>, it also has to check if the effective value of the object is not empty in order to not add empty leaf to the accumulator.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> filterDelete</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">>(</span><span style="color:#FFB86C;font-style:italic">obj</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> T</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> T</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">typeof</span><span style="color:#F8F8F2"> obj </span><span style="color:#FF79C6">!==</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">object</span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> ||</span><span style="color:#F8F8F2"> Array.</span><span style="color:#50FA7B">isArray</span><span style="color:#F8F8F2">(obj)) {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> obj;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#F8F8F2"> Object.</span><span style="color:#50FA7B">keys</span><span style="color:#F8F8F2">(obj)</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">filter</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">key</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#FF79C6"> !</span><span style="color:#50FA7B">shouldAttributeBeCleaned</span><span style="color:#F8F8F2">(obj[key]))</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">reduce</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">res</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">key</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">      const</span><span style="color:#F8F8F2"> value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> T</span><span style="color:#FF79C6"> =</span><span style="color:#50FA7B"> filterDelete</span><span style="color:#F8F8F2">(obj[key]);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">      if</span><span style="color:#F8F8F2"> (value </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#FF79C6"> typeof</span><span style="color:#F8F8F2"> value </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">object</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (Object.</span><span style="color:#50FA7B">keys</span><span style="color:#F8F8F2">(value).length </span><span style="color:#FF79C6">></span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">          res[key] </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> value;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">      } </span><span style="color:#FF79C6">else</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        res[key] </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> value;</span></span>
<span class="line"><span style="color:#F8F8F2">      }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">      return</span><span style="color:#F8F8F2"> res;</span></span>
<span class="line"><span style="color:#F8F8F2">    }, {} </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> T</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">function</span><span style="color:#50FA7B"> shouldAttributeBeCleaned</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">T</span><span style="color:#F8F8F2">>(</span><span style="color:#FFB86C;font-style:italic">attr</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> T</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">typeof</span><span style="color:#F8F8F2"> attr </span><span style="color:#FF79C6">!==</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">object</span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> ||</span><span style="color:#F8F8F2"> Array.</span><span style="color:#50FA7B">isArray</span><span style="color:#F8F8F2">(attr)) {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#BD93F9"> JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">stringify</span><span style="color:#F8F8F2">(attr) </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">stringify</span><span style="color:#F8F8F2">(firebase.firestore.FieldValue.</span><span style="color:#50FA7B">delete</span><span style="color:#F8F8F2">());</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Thanks to this function, I am able to achieve the exact same behavior as if I would fetch the updated document from the database.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#BD93F9"> *</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> firebase </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">firebase/app</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">firebase/firestore</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> firestore </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> firebase.</span><span style="color:#50FA7B">firestore</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> ref </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> firestore.</span><span style="color:#50FA7B">collection</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">users</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">).</span><span style="color:#50FA7B">doc</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">david</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">let</span><span style="color:#F8F8F2"> user </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    description</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> firebase.firestore.FieldValue.</span><span style="color:#50FA7B">delete</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">await</span><span style="color:#F8F8F2"> ref.</span><span style="color:#50FA7B">update</span><span style="color:#F8F8F2">(user);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">filterDelete</span><span style="color:#F8F8F2">(user)); </span><span style="color:#6272A4">// {}</span></span></code></pre><hr />
<h3>Limitation</h3>
<p>The major limitation of this strategy is its dependency on the <a href="https://github.com/firebase/firebase-js-sdk" rel="noopener noreferrer external">Firebase</a> library. After each update, it is worth to check if it still works out as the detection of the method <code>FieldValue.delete()</code> may have to change between versions. It happened to me before, therefore be careful if you would use the function.</p>
<p>I can also recommend, if you would use it, to have a special attention on the error handling between the update and clean, because you may want to avoid the scenario in which the values of the local objects are not equals to their database value (“not in sync”).</p>
<hr />
<h3>Conclusion</h3>
<p>You might notice some potential improvements in the above solution. DeckDeckGo is open source, therefore I would be more than happy to get your contribution to the <a href="https://github.com/deckgo/deckdeckgo/blob/master/studio/src/app/utils/editor/firestore.utils.tsx" rel="noopener noreferrer external">code source of this function</a>. It is still <a href="https://hacktoberfest.digitalocean.com/" rel="noopener noreferrer external">Hacktoberfest</a> 2020 after all 😎.</p>
<p>To infinity and beyond!</p>
<p>David</p>
<hr />
<p>Reach me out on <a href="https://twitter.com/daviddalbusco" rel="noopener noreferrer external">Twitter</a> and, why not, give a try to <a href="https://deckdeckgo.com/" rel="noopener noreferrer external">DeckDeckGo</a> for your next presentations.</p>
<p>It deploys your decks online as Progressive Web Apps and can even push your slides’ source code to GitHub.</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Angular State Management Without RxJS - An Experiment]]></title>
          <description><![CDATA[Angular state management without RxJS - An Experiment. Can it be done with the Stencil Store?]]></description>
          <link>https://daviddalbusco.com/blog/angular-state-management-without-rxjs-an-experiment/</link>
          <pubDate>Mon, 14 Sep 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*cwU_XXxEkjzRiSXxuNYTeQ.png" alt="" / loading="lazy"></p>
<p>Implementing a state management in modern web <a href="https://angular.io/" rel="noopener noreferrer external">Angular</a> applications can be tricky.</p>
<p>There are many libraries, such <a href="https://ngrx.io/" rel="noopener noreferrer external">Ngrx</a>, <a href="https://www.ngxs.io/" rel="noopener noreferrer external">ngxs</a>, <a href="https://datorama.github.io/akita/" rel="noopener noreferrer external">Akita</a>, which can be integrated to manage stores but, these are strongly opinionated and have impact on the architecture of the solution.</p>
<p>If we omit the concept displayed by <a href="https://twitter.com/jbandi" rel="noopener noreferrer external">Jonas Bandi</a> in his interesting <a href="https://medium.jonasbandi.net/the-most-simple-state-management-solution-for-angular-1d32706e6f1c" rel="noopener noreferrer external">article</a>, a common alternative to not using 3rd party libraries, is the development of custom stores with <a href="https://rxjs.dev/" rel="noopener noreferrer external">RxJS</a>.</p>
<p>In both cases, libraries or custom, RxJS is used 🤷‍.</p>
<p>Even though RxJS is a wonderful piece of technology, is nowadays a de facto standard when it comes to Angular development, and installed per default with almost any starter kits, it can be still opted-out.</p>
<p>That’s why I was interested to get to know if it would be possible to develop an Angular application using a modern state management but, <strong>without</strong> using RxJS.</p>
<hr />
<h3>Goals</h3>
<p>To narrow the goals of the experiment, these are those I was looking to test:</p>
<ul>
<li>Can a property be bind and updated in a template, without having to write extra code or trigger the change detection, as it would be solved with an observable?</li>
<li>Can store’s values be accessed in different routes?</li>
<li>Can store’s values be retrieved in child components?</li>
<li>Can store’s values be used in providers?</li>
<li>Is it easy to integrate it in unit tests?</li>
</ul>
<p>Let’s try to answer these questions, but first, let setup another kind of state management.</p>
<hr />
<h3>Stencil Store</h3>
<blockquote>
<p>The <a href="https://github.com/ionic-team/stencil-store" rel="noopener noreferrer external">@stencil/store</a> is a lightweight shared state library by the <a href="https://stenciljs.com/" rel="noopener noreferrer external">StencilJS</a> core team. It implements a simple key/value map that efficiently re-renders components when necessary.</p>
</blockquote>
<p><img src="https://daviddalbusco.com/assets/images/1*QE-QFGXyHgVLowWutgUA5A.gif" alt="" / loading="lazy"></p>
<p>I use it in our web open source editor for presentations, <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>, and I have to admit, I kind of have a crush for this lightweight store. It is so bare minimum simple, and effective, I obviously selected it to perform my experiment.</p>
<p>Even though it would work out of the box with Angular, note that I had to create a <a href="https://github.com/peterpeterparker/stencil-store" rel="noopener noreferrer external">fork</a>. The Webpack’s build was complaining about it and, since we do not need this requirement in case of an Angular usage, I just removed it.</p>
<p>If I, or anyone, would use it for a real application, the library dependency could be patched easily I am guessing.</p>
<hr />
<h3>Source Code</h3>
<p>Before going further, note that this experiment’s source code is available on <a href="https://github.com/peterpeterparker/angular-store-demo" rel="noopener noreferrer external">GitHub</a>.</p>
<hr />
<h3>Setup</h3>
<p>To set up for such a store for an application, we can create a new TypeScript file, such as <code>clicks.store.ts</code> , and use the <code>createStore</code> function exposed by the Stencil Store.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { createStore } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@stencil/store</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> { state } </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> createStore</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    clicks</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 0</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> { state };</span></span></code></pre><p>That’s it. It is the minimum to expose a global <code>clicks</code> store for an app.</p>
<p>Because I was eager to give a try to the few other features of the store, I also added the usage of the functions <code>onChange</code> , to test if property listening to changes would also be re-rendered, and the <code>dispose</code> feature needed for testing purpose.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { createStore } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@stencil/store</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> { state, onChange, reset, dispose } </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> createStore</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    clicks</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    count</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 0</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">onChange</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">clicks</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, (</span><span style="color:#FFB86C;font-style:italic">value</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    state.count </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> value </span><span style="color:#FF79C6">*</span><span style="color:#BD93F9"> 2</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> { state, dispose };</span></span></code></pre><p>Pretty slim in my humble opinion 😉.</p>
<p>It is also worth to notice that it is possible to create as many stores as we would need.</p>
<hr />
<h3>#1: Property Binding And Re-render</h3>
<p>I tried different ways to use the properties of the store in the templates and, figured out that the easiest way was to bind the <code>state</code> with a component’s variable.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Component } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> store </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">../../stores/clicks.store</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    selector</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">app-page1</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    templateUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./page1.component.html</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    styleUrls</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">./page1.component.css</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">]</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> Page1Component</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    state$$ </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> store.state;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>It can then be use in a template to display the values of the store.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">>Clicks: {{state$$.clicks}}&#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">>Count: {{state$$.count}}&#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span></code></pre><p>Does it get re-rendered, when the store changes?</p>
<p>To try out this hypothesis, I added a function to the component, which increments the <code>clicks</code>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">inc</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#F8F8F2">  store.state.clicks</span><span style="color:#FF79C6">++</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Therefore, if everything works as expected, each time I would call the above function, the <code>clicks</code> should be incremented and displayed. Because I registered an <code>onChange</code> on such property, the <code>count</code> should be actualized with twice the value.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*8b1SV-obHx5L-N0dMEfOFg.gif" alt="" / loading="lazy"></p>
<p><strong>Success</strong> ✅</p>
<p>It exactly behaves as expected. Store properties are modified and, the layout is re-rendered. In addition, I did not have to implement any custom change detection calls or what so ever.</p>
<hr />
<h3>#2: Routes</h3>
<p>The second question I was looking to answer, was related to sharing data between routes. To answer it, I created another page component, added it to the routing and used the store exactly in the same way as previously.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Component } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> store </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">../../stores/clicks.store</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    selector</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">app-page2</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    template</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> `&#x3C;h1>Page 2&#x3C;/h1></span></span>
<span class="line"><span style="color:#F1FA8C">        &#x3C;p>Clicks: {{ state$$.clicks }}&#x3C;/p></span></span>
<span class="line"><span style="color:#F1FA8C">        &#x3C;p>Count: {{ state$$.count }}&#x3C;/p>`</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> Page2Component</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    state$$ </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> store.state;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>If this would work out, once I would navigate, I would find the exact same value in each page without having to implement anything else respectively without the need to pass values between routes.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*G-kIoh5UTY9rSAq8bKWWpw.gif" alt="" / loading="lazy"></p>
<p><strong>Success</strong> ✅</p>
<p>Indeed, stores data can be shared between routes.</p>
<hr />
<h3>#3: Components</h3>
<p>Likewise, instead of routes, are data accessible from a component?</p>
<p>To test this hypothesis, I refactored the <code>page2</code> to move the code to a separate component <code>card</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Component } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> store </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">../../stores/clicks.store</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    selector</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">app-card</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    template</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> `&#x3C;p>Clicks: {{ state$$.clicks }}&#x3C;/p></span></span>
<span class="line"><span style="color:#F1FA8C">        &#x3C;p>Count: {{ state$$.count }}&#x3C;/p>`</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    styleUrls</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">./card.component.css</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">]</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> CardComponent</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    state$$ </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> store.state;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>I then used it in <code>page2</code> . Note that doing so, this component, page, does not have to include the store anymore.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Component } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    selector</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">app-page2</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    template</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> `&#x3C;h1>Page 2&#x3C;/h1></span></span>
<span class="line"><span style="color:#F1FA8C">        &#x3C;app-card>&#x3C;/app-card>`</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> Page2Component</span><span style="color:#F8F8F2"> {}</span></span></code></pre><p>As for previous test, this would be validated, if values would be displayed and updated even if use in a child component.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*tKYc1DwBsGcrUU_x4Q8Apg.gif" alt="" / loading="lazy"></p>
<p><strong>Success</strong> ✅</p>
<p>As previously, it works as expected.</p>
<hr />
<h3>#4: Services</h3>
<p>I was asking my self if data could also be used in <code>providers</code> , therefore I added a service to test this specific question.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Injectable } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> store </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">../stores/clicks.store</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Injectable</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    providedIn</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">root</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> AlertService</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    show</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#50FA7B">        alert</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`Count: </span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">store.state.count</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>If I call the service’s function, an alert should be triggered and the current <code>count</code> value of the store should be displayed.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*-KQgSKTCDBWsW7y9jW8ZlA.gif" alt="" / loading="lazy"></p>
<p><strong>Success</strong> ✅</p>
<p>Providers have access to the store.</p>
<hr />
<h3>#5: Test</h3>
<p>In addition to the runtime, I was also curious about the integration in unit tests. Probably even more than the integration in applications, the usage of stores and RxJS in tests can be also tricky.</p>
<p>Therefore, I created a test which should increment the <code>clicks</code> and validate that the value has, well, been incremented.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { ComponentFixture, TestBed } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core/testing</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Page1Component } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./page1.component</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> store </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">../../stores/clicks.store</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">describe</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Page1Component</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  let</span><span style="color:#F8F8F2"> component</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Page1Component</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">  let</span><span style="color:#F8F8F2"> fixture</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> ComponentFixture</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Page1Component</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  beforeEach</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#F8F8F2"> TestBed.</span><span style="color:#50FA7B">configureTestingModule</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">      declarations</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [ Page1Component ]</span></span>
<span class="line"><span style="color:#F8F8F2">    })</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">compileComponents</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  beforeEach</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    fixture </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> TestBed.</span><span style="color:#50FA7B">createComponent</span><span style="color:#F8F8F2">(Page1Component);</span></span>
<span class="line"><span style="color:#F8F8F2">    component </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> fixture.componentInstance;</span></span>
<span class="line"><span style="color:#F8F8F2">    fixture.</span><span style="color:#50FA7B">detectChanges</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  beforeEach</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    store.</span><span style="color:#50FA7B">dispose</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  it</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">should create</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    expect</span><span style="color:#F8F8F2">(component).</span><span style="color:#50FA7B">toBeTruthy</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  it</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">should increment</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    component.</span><span style="color:#50FA7B">inc</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    fixture.</span><span style="color:#50FA7B">detectChanges</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> paragraph </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#F8F8F2">          fixture.nativeElement.</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">p:first-of-type</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    expect</span><span style="color:#F8F8F2">(paragraph.textContent).</span><span style="color:#50FA7B">toEqual</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Clicks: 1</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><p>If this would be correct, the test should pass.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*xumQL55FR4t0S9XCU0spqA.png" alt="" / loading="lazy"></p>
<p><img src="https://daviddalbusco.com/assets/images/1*PsZJK-IrkOwtSWGRCmNbNQ.png" alt="" / loading="lazy"></p>
<p><strong>Success</strong> ✅</p>
<p>It is possible to use the store in unit tests and thus, without any particular headache. It works in tests the same manner as it work when used in the application.</p>
<hr />
<h3>Summary</h3>
<p>All hypothesis, re-rendering data, accessing these and testing the store were a success ✅.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*KE-3sINtqGTzJ4JgEcw0yA.gif" alt="" / loading="lazy"></p>
<hr />
<h3>Considerations</h3>
<p>The scope of this experiment was to some extension, limited and, it might need a bit more analysis before being applied to a real life application. I think in particular at the following questions:</p>
<ul>
<li>Would it be possible to scope the store, not to the root, but to a particular module? Even though providers provided in <code>root</code> are often used, I think, it would be a nice add-on.</li>
<li>How does the rendering performs with a lot of nodes contained in the store? My spontaneous guess is that it behaves exactly like it would behave with or without any other stores but, it is probably worth a try to go a step further and try to render a lot of information.</li>
<li>What’s the cost of the Stencil Store in comparison to any other libraries based on RxJS or RxJS itself. If I would have to bet right now, I would bet on the fact that the Stencil Store is maybe the lightest. According <a href="https://bundlephobia.com/result?p=@stencil/store@1.3.0" rel="noopener noreferrer external">bundlephobia</a>, it costs only 899 bytes (minified + gzipped) 🤯.</li>
<li>Stencil is server side rendering (SSR) and pre-rendering <a href="https://stenciljs.com/docs/static-site-generation-server-side-rendering-ssr" rel="noopener noreferrer external">compatible</a>. Therefore, as the store has been developed in first place for such technology, I am guessing that it would be also the case with Angular. However, this would have to be tested too.</li>
</ul>
<p>If you are interested in these questions, let me know. I would love to hear from you, to get your feedbacks and would be happy to continue the experiment 😃.</p>
<hr />
<h3>Take Away</h3>
<p>Honestly? I am that close to find a new idea of application, just to try out concretely the Stencil Store in a modern web Angular application. There is often no better way to experiment, than developing a real application.</p>
<p>To infinity and beyond!</p>
<p>David</p>
<hr />
<p>Reach me out on <a href="https://twitter.com/daviddalbusco" rel="noopener noreferrer external">Twitter</a> and, why not, give a try to <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a> for your next presentations!</p>
<p>It deploys your decks online as Progressive Web Apps and can even push your slides’ source code to GitHub.</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Firebase Cloud Functions: Git Commands & GitHub GraphQL API]]></title>
          <description><![CDATA[Execute Git commands, such as Clone or Push, and use the GitHub GraphQL API, to create a Pull Request, in Firebase Cloud Functions.]]></description>
          <link>https://daviddalbusco.com/blog/firebase-cloud-functions-git-commands-github-graphql-api/</link>
          <pubDate>Tue, 08 Sep 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*CLLQh8vkxSkbYBCoXjU95A.png" alt="" / loading="lazy"></p>
<p><em>Background photo by <a href="https://unsplash.com/@goumbik?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Lukas Blazek</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>We recently released an exciting new unique feature at <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>.</p>
<p>In addition to being able to deploy online your presentations as Progressive Web Apps, our web open source editor can now push their source codes to <a href="https://github.com" rel="noopener noreferrer external">GitHub</a> too 🎉.</p>
<iframe width="280" height="158" src="https://www.youtube.com/embed/yVYEacO7qrQ" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
<br/>
<p>This new function runs in <a href="https://firebase.google.com/docs/functions/" rel="noopener noreferrer external">Firebase Cloud Functions</a>. Because we like to share our discoveries, here are the key elements we learned while developing this integration.</p>
<hr />
<h3>Access Tokens</h3>
<p>To interact with GitHub we need a token.</p>
<hr />
<h4>Personal Token</h4>
<p>If you are looking to interact with GitHub with your account, your can use a <a href="https://github.com/settings/tokens" rel="noopener noreferrer external">personal access token</a>. Once created, you can set in the configuration of our Firebase functions. Doing so, it will be obfuscated from your code.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">#!/bin/sh</span></span>
<span class="line"><span style="color:#50FA7B">firebase</span><span style="color:#F1FA8C"> functions:config:set</span><span style="color:#F1FA8C"> github.token=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">4a686.......</span><span style="color:#E9F284">"</span></span></code></pre><hr />
<h4>Firebase Auth And GitHub Token</h4>
<p>If you are rather interested to interact with GitHub in behave of your users, you might use <a href="https://github.com/firebase/FirebaseUI-Web" rel="noopener noreferrer external">Firebase UI</a> and the <a href="https://firebase.google.com/docs/auth/" rel="noopener noreferrer external">Firebase Authentication</a>.</p>
<p>As far as I discovered, with such combination, it is unfortunately not possible to get the user’s GitHub token in a Firebase Cloud Functions. I tried to hook on the authentication <a href="https://firebase.google.com/docs/functions/auth-events" rel="noopener noreferrer external">events</a> but did not find any related information in the object triggered.</p>
<p>I might have missed something, in such a case please let me know as soon as possible (!), but if not, to get such information, you have to find it through the <code>signInSuccessWithAuthResult</code> callback of the Firebase UI configuration.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">callbacks: {</span></span>
<span class="line"><span style="color:#F8F8F2">  signInSuccessWithAuthResult:</span></span>
<span class="line"><span style="color:#F8F8F2">    (</span><span style="color:#FFB86C;font-style:italic">authResult</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> firebase</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">auth</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">UserCredential</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">_redirectUrl</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> token</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#F8F8F2">      (userCred.credential </span><span style="color:#FF79C6">as</span></span>
<span class="line"><span style="color:#F8F8F2">               firebase.auth.OAuthCredential).accessToken;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  },</span></span>
<span class="line"><span style="color:#F8F8F2">},</span></span></code></pre><p><em>Note that I opened an issue to ask how it was possible to access the token using TypeScript and the cast to <em><code>OAuthCredential</code></em> was provided as <a href="https://github.com/firebase/firebaseui-web/issues/743" rel="noopener noreferrer external">answer</a>.</em></p>
<hr />
<h3>File System</h3>
<p>Before going further, you may ask yourself how we are going to be able to execute Git command in the &quot;cloud&quot;? I was actually asking my self the same question, and it turns out that Firebase Functions have access to a temporary folder of their <a href="https://cloud.google.com/functions/docs/concepts/exec#file_system" rel="noopener noreferrer external">file system</a>.</p>
<blockquote>
<p>The only writeable part of the filesystem is the <code>/tmp</code> directory, which you can use to store temporary files in a function instance. This is a local disk mount point known as a &quot;tmpfs&quot; volume in which data written to the volume is stored in memory. Note that it will consume memory resources provisioned for the function.</p>
</blockquote>
<p>In addition, temporary directories are not share across functions. It means for example that you cannot use such a folder to share data.</p>
<p>The <code>tmp</code> order has not to be hardcoded. Instead of such, the <a href="https://nodejs.org/api/os.html" rel="noopener noreferrer external">Node.js OS module</a> can be used to retrieve the temporary folder. It can be more handy to it if for some reason it would change in the future, you never know 😉.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#BD93F9"> *</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> os </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">os</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(os.</span><span style="color:#50FA7B">tmpdir</span><span style="color:#F8F8F2">()); </span><span style="color:#6272A4">// -> /tmp</span></span></code></pre><p>Using it together with the <a href="https://nodejs.org/api/path.html" rel="noopener noreferrer external">Path module</a>, we can even create a short utility function to resolve files’ paths locally.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#BD93F9"> *</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> path </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">path</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#BD93F9"> *</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> os </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">os</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> getFilePath</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">...</span><span style="color:#FFB86C;font-style:italic">files</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">[])</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#F8F8F2"> path.</span><span style="color:#50FA7B">join</span><span style="color:#F8F8F2">(os.</span><span style="color:#50FA7B">tmpdir</span><span style="color:#F8F8F2">(), </span><span style="color:#FF79C6">...</span><span style="color:#F8F8F2">files);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">getFilePath</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">yo</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">david.txt</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">); </span><span style="color:#6272A4">// -> /tmp/yo/david.txt</span></span></code></pre><hr />
<h3>Git Commands</h3>
<p>In order to clone a repo, or generally speaking to execute any Git commands such as commit, pull or push, I suggest the use the <a href="https://github.com/steveukx" rel="noopener noreferrer external">simple-git</a> interface for Node.js developed by <a href="https://github.com/steveukx" rel="noopener noreferrer external">Steve King</a> (1.5 millions weekly downloads on <a href="https://www.npmjs.com/package/simple-git" rel="noopener noreferrer external">npm</a>). It really eases all the work.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> i</span><span style="color:#F1FA8C"> simple-git</span><span style="color:#BD93F9"> --save</span></span></code></pre><hr />
<h4>Clone</h4>
<p>Concretely, a clone function can be implemented as following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#BD93F9"> *</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> path </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">path</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#BD93F9"> *</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> os </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">os</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> simpleGit, {SimpleGit} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">simple-git</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> clone</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">repoUrl</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">repoName</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> localPath</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> path.</span><span style="color:#50FA7B">join</span><span style="color:#F8F8F2">(os.</span><span style="color:#50FA7B">tmpdir</span><span style="color:#F8F8F2">(), repoName);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  await</span><span style="color:#50FA7B"> deleteDir</span><span style="color:#F8F8F2">(localPath);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> git</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> SimpleGit</span><span style="color:#FF79C6"> =</span><span style="color:#50FA7B"> simpleGit</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">  await</span><span style="color:#F8F8F2"> git.</span><span style="color:#50FA7B">clone</span><span style="color:#F8F8F2">(repoUrl, localPath);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// Demo:</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> clone</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">https://github.com/deckgo/deckdeckgo/</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">deckdeckgo</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">})();</span></span></code></pre><p>Even though temporary folder are probably going to be empty, it is probably a safe bet to try to delete the working subdirectory first. That’s why I call the <code>deleteDir</code> in the above function.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#BD93F9"> *</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> rimraf </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">rimraf</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> deleteDir</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">localPath</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">void</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">void</span><span style="color:#F8F8F2">>((</span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    rimraf</span><span style="color:#F8F8F2">(localPath, () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">      resolve</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>As you can notice, I use <a href="https://github.com/isaacs/rimraf" rel="noopener noreferrer external">rimraf</a> from <a href="https://twitter.com/izs" rel="noopener noreferrer external">Isaac Z. Schlueter</a> (37 millions weekly downloads on <a href="https://www.npmjs.com/package/rimraf" rel="noopener noreferrer external">npmjs</a>).</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> i</span><span style="color:#F1FA8C"> rimraf</span><span style="color:#BD93F9"> --save</span><span style="color:#F8F8F2"> &#x26;&#x26; </span><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> i</span><span style="color:#F1FA8C"> @types/rimraf</span><span style="color:#BD93F9"> --save-dev</span></span></code></pre><hr />
<h4>Push</h4>
<p>Another interesting example of Git commands is the Push request, as we do have to use the token to authenticate the request.</p>
<p>After searching for a solution to use the token, I notably spent some times reading this <a href="https://stackoverflow.com/questions/18935539/authenticate-with-github-using-a-token" rel="noopener noreferrer external">Stackoverflow</a> question and answers, I came to the conclusion that the solution which gives the best results, to avoid exposing the token, even though we are executing the interaction in the function, was to use it in the Git URI.</p>
<p>Note that the token is exposed in the potential error messages, that is why I think it is also good to catch properly these too.</p>
<p>In addition to the token, we might need to provide our GitHub account’s <code>username</code> (such as <a href="https://github.com/peterpeterparker" rel="noopener noreferrer external">peterpeterparker</a> for example) and <code>email</code>. These information can be administrated with the configuration of our functions too.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#BD93F9"> *</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> functions </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">firebase-functions</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#BD93F9"> *</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> path </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">path</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#BD93F9"> *</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> os </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">os</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> simpleGit, {SimpleGit} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">simple-git</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> push</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">project</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#FFB86C;font-style:italic">                           branch</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">  try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> localPath</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> path.</span><span style="color:#50FA7B">join</span><span style="color:#F8F8F2">(os.</span><span style="color:#50FA7B">tmpdir</span><span style="color:#F8F8F2">(), repoName);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // Git needs to know where is has to run, that's why we pass</span></span>
<span class="line"><span style="color:#6272A4">    // the pass to the constructor of simple-git</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> git</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> SimpleGit</span><span style="color:#FF79C6"> =</span><span style="color:#50FA7B"> getSimpleGit</span><span style="color:#F8F8F2">(localPath);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // Configure Git with the username and email</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> username</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> functions.</span><span style="color:#50FA7B">config</span><span style="color:#F8F8F2">().github.username;</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> email</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> functions.</span><span style="color:#50FA7B">config</span><span style="color:#F8F8F2">().github.email;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#F8F8F2"> git.</span><span style="color:#50FA7B">addConfig</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">user.name</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, username);</span></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#F8F8F2"> git.</span><span style="color:#50FA7B">addConfig</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">user.email</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, email);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // Finally Git push</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> token</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> functions.</span><span style="color:#50FA7B">config</span><span style="color:#F8F8F2">().github.token;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#F8F8F2"> git.</span><span style="color:#50FA7B">push</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`https://</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">username</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">:</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">token</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">@github.com/</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">username</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">/</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">project</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">.git`</span><span style="color:#F8F8F2">, branch);</span></span>
<span class="line"><span style="color:#F8F8F2">  } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#FF79C6">    throw</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Error</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`Error pushing.`</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// Demo:</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> push</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">deckdeckgo</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">my-branch</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">})();</span></span></code></pre><hr />
<h3>GitHub GraphQL API</h3>
<p>The last, or new, depends on the point of view, version (v4) of the GitHub API can be use with GraphQL queries. Its <a href="https://docs.github.com/en/graphql" rel="noopener noreferrer external">documentation</a> makes it relatively easy to search for information but the <a href="https://developer.github.com/v4/explorer/" rel="noopener noreferrer external">explorer</a>, and its auto-complete feature, is probably even more handy to compose quickly flexible queries.</p>
<hr />
<h4>Query</h4>
<p>I did not use any GraphQL clients (as for example <a href="https://github.com/apollographql/apollo-client" rel="noopener noreferrer external">Apollo</a>) to perform the queries. Instead, I developed a utility to perform the HTTPS requests.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> fetch, {Response} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">node-fetch</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> queryGitHub</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">githubToken</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#FFB86C;font-style:italic">                           query</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Response</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> githubApiV4</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">https://api.github.com/graphql</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> rawResponse</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Response</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> fetch</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">githubApiV4</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">, {</span></span>
<span class="line"><span style="color:#F8F8F2">    method</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">POST</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    headers</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">      Accept</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">application/json</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">      '</span><span style="color:#F1FA8C">Content-Type</span><span style="color:#E9F284">'</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">application/json</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">      Authorization</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> `token </span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">githubToken</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    },</span></span>
<span class="line"><span style="color:#F8F8F2">    body</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">stringify</span><span style="color:#F8F8F2">({query}),</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">rawResponse </span><span style="color:#FF79C6">||</span><span style="color:#FF79C6"> !</span><span style="color:#F8F8F2">rawResponse.ok) {</span></span>
<span class="line"><span style="color:#FF79C6">    throw</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Error</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Cannot perform GitHub query.</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#F8F8F2"> rawResponse;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>As <code>fetch</code> is not natively available in <a href="https://nodejs.org/en/" rel="noopener noreferrer external">Node.js</a>, I used <a href="https://github.com/node-fetch/node-fetch" rel="noopener noreferrer external">node-fetch</a> (16 millions weekly downloads on <a href="https://www.npmjs.com/package/node-fetch" rel="noopener noreferrer external">npm</a>).</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> i</span><span style="color:#F1FA8C"> node-fetch</span><span style="color:#BD93F9"> --save</span><span style="color:#F8F8F2"> &#x26;&#x26; </span><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> i</span><span style="color:#F1FA8C"> @types/node-fetch</span><span style="color:#BD93F9"> --save-dev</span></span></code></pre><hr />
<h4>Query: User Information</h4>
<p>A relatively basic example of query can be the following. In such function, we try to retrieve the GitHub <code>login</code> (&quot;username&quot;) and <code>id</code> corresponding to the token we are using to authenticate the request, respectively the information of the <a href="https://docs.github.com/en/graphql/reference/queries#user" rel="noopener noreferrer external">currently authenticated user</a>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> interface</span><span style="color:#8BE9FD;font-style:italic"> GitHubUser</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  id</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  login</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> getUser</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">githubToken</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">GitHubUser</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">GitHubUser</span><span style="color:#F8F8F2">>(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">reject</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">      const</span><span style="color:#F8F8F2"> query </span><span style="color:#FF79C6">=</span><span style="color:#F1FA8C"> `</span></span>
<span class="line"><span style="color:#F1FA8C">        query {</span></span>
<span class="line"><span style="color:#F1FA8C">          viewer {</span></span>
<span class="line"><span style="color:#F1FA8C">            id,</span></span>
<span class="line"><span style="color:#F1FA8C">            login</span></span>
<span class="line"><span style="color:#F1FA8C">          }</span></span>
<span class="line"><span style="color:#F1FA8C">        }</span></span>
<span class="line"><span style="color:#F1FA8C">      `</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">      const</span><span style="color:#F8F8F2"> response</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Response</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#FF79C6">            await</span><span style="color:#50FA7B"> queryGitHub</span><span style="color:#F8F8F2">(githubToken, query);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">      const</span><span style="color:#F8F8F2"> result </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> response.</span><span style="color:#50FA7B">json</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">      resolve</span><span style="color:#F8F8F2">(result.data.viewer);</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#50FA7B">      reject</span><span style="color:#F8F8F2">(err);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// Demo:</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> token</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> functions.</span><span style="color:#50FA7B">config</span><span style="color:#F8F8F2">().github.token;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> user </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> getUser</span><span style="color:#F8F8F2">(token);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2"> console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(user); </span><span style="color:#6272A4">// -> {login: 'peterpeterparker', id: '123'}</span></span>
<span class="line"><span style="color:#F8F8F2">})();</span></span></code></pre><hr />
<h4>Mutation: Pull Request</h4>
<p>Creating a Pull Request is not a GraphQL query but a <a href="https://docs.github.com/en/graphql/reference/mutations#createpullrequest" rel="noopener noreferrer external">mutation</a>. It needs a bit more information in comparison to previous query, but the logic behind is the same: compose a GraphQL query/mutation, send it through an HTTPS request and get the results 😁.</p>
<p>It is worth to notice that, in order to create a PR, the mutation will need a <code>repositoryId</code> . This information can be found with the help of another GraphQL query, as for example provided when requesting <a href="https://docs.github.com/en/graphql/reference/queries#repository" rel="noopener noreferrer external">repository</a> information.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> createPR</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">githubToken</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#FFB86C;font-style:italic">                         repositoryId</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#FFB86C;font-style:italic">                         branch</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">void</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">void</span><span style="color:#F8F8F2">>(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">reject</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">      const</span><span style="color:#F8F8F2"> title</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">feat: my title</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">            const</span><span style="color:#F8F8F2"> body</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#F1FA8C"> `# The Pull Request body.</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F1FA8C">      It supports *Markdown*.`</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">     // We want to provide a PR from a branch to master</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">     const</span><span style="color:#F8F8F2"> query </span><span style="color:#FF79C6">=</span><span style="color:#F1FA8C"> `</span></span>
<span class="line"><span style="color:#F1FA8C">             mutation CreatePullRequest {</span></span>
<span class="line"><span style="color:#F1FA8C">               createPullRequest(input:{baseRefName:"master",body:"</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">body</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">",headRefName:"</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">branch</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">",repositoryId:"</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">repositoryId</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">",title:"</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">title</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">"}) {</span></span>
<span class="line"><span style="color:#F1FA8C">                 pullRequest {</span></span>
<span class="line"><span style="color:#F1FA8C">                   id</span></span>
<span class="line"><span style="color:#F1FA8C">                 }</span></span>
<span class="line"><span style="color:#F1FA8C">               }</span></span>
<span class="line"><span style="color:#F1FA8C">             }</span></span>
<span class="line"><span style="color:#F1FA8C">           `</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">      const</span><span style="color:#F8F8F2"> response</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Response</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#FF79C6">            await</span><span style="color:#50FA7B"> queryGitHub</span><span style="color:#F8F8F2">(githubToken, query);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">      const</span><span style="color:#F8F8F2"> result </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> response.</span><span style="color:#50FA7B">json</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">      if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">result </span><span style="color:#FF79C6">||</span><span style="color:#FF79C6"> !</span><span style="color:#F8F8F2">result.data </span><span style="color:#FF79C6">||</span></span>
<span class="line"><span style="color:#FF79C6">          !</span><span style="color:#F8F8F2">result.data.createPullRequest </span><span style="color:#FF79C6">||</span><span style="color:#F8F8F2"> result.errors) {</span></span>
<span class="line"><span style="color:#50FA7B">        resolve</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">undefined</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">      }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">      resolve</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#50FA7B">      reject</span><span style="color:#F8F8F2">(err);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// Demo:</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> token</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> functions.</span><span style="color:#50FA7B">config</span><span style="color:#F8F8F2">().github.token;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> createPR</span><span style="color:#F8F8F2">(token, </span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">6789</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">my-branch</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">})();</span></span></code></pre><hr />
<h3>Summary</h3>
<p>I learned many new things while developing this feature and, I hope that with the help of this blog post, I was able to share the major learnings.</p>
<p>In addition, we are open source, you can always have a look at our <a href="https://github.com/deckgo/deckdeckgo/" rel="noopener noreferrer external">repo</a>’s source code or contribute to our project.</p>
<p>You are also most welcomed to give a try to <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a> for your next presentations.</p>
<p>I am also looking forward to checkout and give a try to the GitHub repos that will contain the source code of your slides 😉.</p>
<p>To infinity and beyond!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Firebase Cloud Functions: Verify Users Tokens]]></title>
          <description><![CDATA[Grant access to your Firebase Cloud Functions only for authenticated users.]]></description>
          <link>https://daviddalbusco.com/blog/firebase-cloud-functions-verify-users-tokens/</link>
          <pubDate>Fri, 04 Sep 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*BSLn4cG8OsG63z4pbJqrXA.jpeg" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@nxvision?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Nigel Tadyanehondo</a> on <a href="https://unsplash.com/s/photos/you-shall-not-pass?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I started yesterday the refactoring of one of the core functions of <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a> by declaring a new <a href="https://firebase.google.com/docs/functions/" rel="noopener noreferrer external">Firebase Cloud Functions</a>, which can be triggered via <a href="https://firebase.google.com/docs/functions/http-events" rel="noopener noreferrer external">HTTP requests</a>.</p>
<p>As I was looking to protect its access, in order to avoid sneaky requests, I followed one of my <a href="https://medium.com/better-programming/protect-your-http-firebase-cloud-functions-adf23c45765e" rel="noopener noreferrer external">previous blog post</a> to protect it with the help of a bearer.</p>
<p>Once I tested this first step of the feature, I actually noticed that it was not the correct solution for my use case. I rather had to grant the access using the users tokens.</p>
<hr />
<h3>Verify Users’ Tokens In Cloud Functions</h3>
<p>It will probably sound silly for those who know the solution, but it actually took me quite some time to find how to verify the tokens of the users in Firebase Cloud Functions.</p>
<p>I took a wrong start by trying to implement the solution as I was implementing it in a backend respectively as displayed in the <a href="https://developers.google.com/identity/sign-in/web/backend-auth" rel="noopener noreferrer external">Authenticate with a backend server</a> using the library <a href="https://github.com/googleapis/google-auth-library-nodejs" rel="noopener noreferrer external">google-auth-library</a>. I spent time implementing the solution and finding where I could find the requested OAuth <code>CLIENT_ID</code> information of my projects to finally face the following error while I was trying the process:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">No</span><span style="color:#F1FA8C"> pem</span><span style="color:#F1FA8C"> found</span><span style="color:#F1FA8C"> for</span><span style="color:#F1FA8C"> envelope:</span><span style="color:#F1FA8C"> {</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">alg</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">:</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">RS256</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">,</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">kid</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">:</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">...</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">,</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">typ</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">:</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">JWT</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">}</span></span></code></pre><p>Finally, after many tries, I accepted the defeat and googled for solutions. Fortunately, for me, at the end of a <a href="https://stackoverflow.com/questions/61937587/how-to-get-valid-token-from-react-firebase-f%c3%bcr-nodesjs-server-verification/61937783#comment112690479_61937783" rel="noopener noreferrer external">Stackoverflow question</a>, I discovered, thanks to an answer of <a href="https://stackoverflow.com/users/8535518/will" rel="noopener noreferrer external">Will</a>, that there was a way easier to verify the tokens.</p>
<p>Indeed, if I had known the <a href="https://firebase.google.com/docs/auth/admin/verify-id-tokens" rel="noopener noreferrer external">Admin documentation</a>, I would have discovered that Firebase as a built-in method to solve this requirement.</p>
<blockquote>
<p>The Firebase Admin SDK has a built-in method for verifying and decoding ID tokens. If the provided ID token has the correct format, is not expired, and is properly signed, the method returns the decoded ID token. You can grab the uid of the user or device from the decoded token.</p>
</blockquote>
<p>Once I discovered this gem and, once my brain finally clicked, I was able to implement a small utility function:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#BD93F9"> *</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> admin </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">firebase-admin</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#BD93F9"> *</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> functions </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">firebase-functions</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> verifyToken</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#FFB86C;font-style:italic">                request</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> functions</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">Request</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">boolean</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">  try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> token</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> getToken</span><span style="color:#F8F8F2">(request);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">token) {</span></span>
<span class="line"><span style="color:#FF79C6">      return</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> payload</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> admin</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">auth</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">DecodedIdToken</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#FF79C6">                   await</span><span style="color:#F8F8F2"> admin.</span><span style="color:#50FA7B">auth</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">verifyIdToken</span><span style="color:#F8F8F2">(token);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> payload </span><span style="color:#FF79C6">!==</span><span style="color:#BD93F9"> null</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> getToken</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">request</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> functions</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">Request</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">                       Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">request.headers.authorization) {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> token</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#F8F8F2">        request.headers.authorization.</span><span style="color:#50FA7B">replace</span><span style="color:#F8F8F2">(</span><span style="color:#FF5555">/</span><span style="color:#FF79C6">^</span><span style="color:#F1FA8C">Bearer</span><span style="color:#BD93F9">\s</span><span style="color:#FF5555">/</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">''</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#F8F8F2"> token;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Note that I test if the <code>payload</code> is not <code>null</code> to consider the token as valid but, I think that it might be not needed. The method <code>verifyIdToken</code> throw an error when it is not valid.</p>
<p>In addition, you can also notice that I except the user’s token to be passed as in the <code>headers</code> of the HTTP request and prefixed with a keyword <code>Bearer</code>.</p>
<p>Given for example a token ID <code>975dd9f6</code> , an HTTP POST request would look like the following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">#!/bin/sh</span></span>
<span class="line"><span style="color:#50FA7B">    curl</span><span style="color:#BD93F9"> -i</span></span>
<span class="line"><span style="color:#50FA7B">         -H</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Accept: application/json</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B">         -H</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Authorization: Bearer 975dd9f6</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B">         -X</span><span style="color:#F1FA8C"> POST</span><span style="color:#F1FA8C"> https://us-central1-yolo.cloudfunctions.net/helloWorld</span></span></code></pre><hr />
<h3>Grant Only Not Anonymous Users</h3>
<p>Anyone can try out <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>, there is no mandatory login or sign-in upfront if you just want to give it a try. It is something really important to us, we are not chasing data or number of users, we are developing an editor for presentations for users’ who want to use it, or not 😉.</p>
<p>That being said, if users want to share publicly their presentations, because we don’t want to have too much “This is a test” or “Yolo” decks publicly published, respectively avoid if possible not meaningful public content, we do restrict our “publishing process” (the one in which we transform and deploy the presentations online as Progressive Web Apps), to signed users.</p>
<p>For these processes, we are using the ability given by Firebase to use <a href="https://firebase.google.com/docs/auth/web/anonymous-auth" rel="noopener noreferrer external">anonymous users</a>.</p>
<p>That’s why, in addition to verify the tokens, I also add to check this information. Fortunately, this can also be solved easily, as the <code>payload</code> provided by the <code>verifyToken</code> function does contain such information.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> payload</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> admin</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">auth</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">DecodedIdToken</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#FF79C6">                   await</span><span style="color:#F8F8F2"> admin.</span><span style="color:#50FA7B">auth</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">verifyIdToken</span><span style="color:#F8F8F2">(token);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">return</span><span style="color:#F8F8F2"> payload </span><span style="color:#FF79C6">!==</span><span style="color:#BD93F9"> null</span><span style="color:#FF79C6"> &#x26;&#x26;</span></span>
<span class="line"><span style="color:#F8F8F2">       payload.firebase.sign_in_provider </span><span style="color:#FF79C6">!==</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">anonymous</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span></code></pre><hr />
<h3>Call Function With Bearer</h3>
<p>In case you would be interested, here is how I provide the above <code>bearer</code> to a function call in TypeScript and in an application which use Firebase Auth.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">helloWorld</span><span style="color:#F8F8F2">(): </span><span style="color:#8BE9FD;font-style:italic">Promise</span><span style="color:#FF79C6">&#x3C;void></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  return new </span><span style="color:#50FA7B">Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">void</span><span style="color:#F8F8F2">>(</span><span style="color:#FFB86C;font-style:italic">async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">reject</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">    try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">      const</span><span style="color:#F8F8F2"> token</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#FF79C6">            await</span><span style="color:#F8F8F2"> firebase.</span><span style="color:#50FA7B">auth</span><span style="color:#F8F8F2">().currentUser.</span><span style="color:#50FA7B">getIdToken</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">      const</span><span style="color:#F8F8F2"> functionsUrl</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#E9F284">           '</span><span style="color:#F1FA8C">https://us-central1-yolo.cloudfunctions.net</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">      const</span><span style="color:#F8F8F2"> rawResponse</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Response</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#FF79C6">            await</span><span style="color:#50FA7B"> fetch</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">functionsUrl</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">/helloWorld`</span><span style="color:#F8F8F2">, {</span></span>
<span class="line"><span style="color:#F8F8F2">        method</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">POST</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        headers</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">          Accept</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">application/json</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">          '</span><span style="color:#F1FA8C">Content-Type</span><span style="color:#E9F284">'</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">application/json</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">          Authorization</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> `Bearer </span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">token</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        },</span></span>
<span class="line"><span style="color:#F8F8F2">        body</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">stringify</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">          something</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">a value</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">        }),</span></span>
<span class="line"><span style="color:#F8F8F2">      });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">      if</span><span style="color:#F8F8F2"> (!</span><span style="color:#FFB86C;font-style:italic">rawResponse</span><span style="color:#F8F8F2"> || !rawResponse.ok) {</span></span>
<span class="line"><span style="color:#50FA7B">        reject</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Post failed etc.</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">      }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">      resolve</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    } catch (err) {</span></span>
<span class="line"><span style="color:#50FA7B">      reject</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">err</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>Cherry On Top: CORS</h3>
<p>Since I was implementing our first function to handle HTTP request, I had to deal with CORS. A quick Google search and a <a href="https://gist.github.com/mediavrog/49c4f809dffea4e00738a7f5e3bbfa59#gistcomment-2585600" rel="noopener noreferrer external">Gist</a> provided by <a href="https://github.com/CoderTonyB" rel="noopener noreferrer external">CoderTonyB</a> provided a solution.</p>
<p>The <a href="https://github.com/expressjs/cors" rel="noopener noreferrer external">expressjs/cors</a> should be installed in the functions’ project.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> i</span><span style="color:#F1FA8C"> cors</span><span style="color:#BD93F9"> --save</span><span style="color:#F8F8F2"> &#x26;&#x26; </span><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> i</span><span style="color:#F1FA8C"> @types/cors</span><span style="color:#BD93F9"> --save-dev</span></span></code></pre><p>Finally, a handler should be used, before the effective implementation, to process the CORS request.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#BD93F9"> *</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> functions </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">firebase-functions</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#BD93F9"> *</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> cors </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">cors</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> helloWorld </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> functions.https.</span><span style="color:#50FA7B">onRequest</span><span style="color:#F8F8F2">(myHelloWorld);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> helloWorld</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">request</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> functions</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">Request</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#FFB86C;font-style:italic">                          response</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> functions</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">Response</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">any</span><span style="color:#F8F8F2">>) {</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> corsHandler </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> cors</span><span style="color:#F8F8F2">({origin</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  corsHandler</span><span style="color:#F8F8F2">(request, response, </span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">      response.</span><span style="color:#50FA7B">send</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Yolo</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>Take Away</h3>
<p>Needless to say, it is actually easy to begin the development of a new feature wrongly and to lose quickly time. I would love to say that taking a deep breath or doing a break is the key, but once in a while it happens, stuffs happens 😉. Nevertheless, if you have got awesome tips and tricks to avoid such scenario, let me know, I am curious to hear about these!</p>
<p>If you are curious about the outcome, follow us on <a href="https://twitter.com/deckdeckgo" rel="noopener noreferrer external">Twitter</a> as we might release a super cool feature for developers next week 🚀.</p>
<p>To infinity and beyond!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Send Email From Firebase Cloud Functions]]></title>
          <description><![CDATA[How to send email with HTML content from Firebase Cloud functions]]></description>
          <link>https://daviddalbusco.com/blog/send-email-from-firebase-cloud-functions/</link>
          <pubDate>Wed, 19 Aug 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*dsCLua-wdb1qDvvAfz8MmA.jpeg" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@lunarts?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Volodymyr Hryshchenko</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>As you can probably imagine, at <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>, we do not have any collaborator who check that the publicly, published, slides have descent content. Neither do we have implemented a machine learning robot which would do so, yet.</p>
<p>I am taking care of such a task manually. I have to add, it makes me happy to do so. All the presentations published so far are always interesting.</p>
<p>Nevertheless, I have to be informed, when such decks are published. That’s why I have implemented a <a href="https://firebase.google.com/docs/functions" rel="noopener noreferrer external">Firebase Cloud Functions</a> to send my self an email with all the information I need to quickly review the new content.</p>
<hr />
<h3>Setup A New Cloud Function</h3>
<p>I assume you already have a Firebase project and, also have already created some functions. If not, you can follow the following <a href="https://firebase.google.com/docs/functions/get-started" rel="noopener noreferrer external">guide</a> to get started.</p>
<p>Moreover, note that I am using <a href="https://www.typescriptlang.org/" rel="noopener noreferrer external">TypeScript</a>.</p>
<hr />
<h3>Let’s Get Started</h3>
<p>A function needs a trigger, that’s why we are registering a function in <code>index.ts</code> on a collection called, for example, <code>demo</code> (of course your collection can have a different name).</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#BD93F9"> *</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> functions </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">firebase-functions</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> watchCreate </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> functions.firestore</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">document</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">demo/{demoId}</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">onCreate</span><span style="color:#F8F8F2">(onCreateSendEmail);</span></span></code></pre><p>We can use any other triggers or lifecycle, not necessary the <code>create</code> one.</p>
<p>To respond to the trigger's execution, we declare a new function which retrieve the newly created value (<code>const demo = snap.data()</code> ) and are adding, for now on, a <code>TODO</code> which should be replaced with the effective method to send email.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { EventContext } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">firebase-functions</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { DocumentSnapshot } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">firebase-functions/lib/providers/firestore</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">interface</span><span style="color:#8BE9FD;font-style:italic"> Demo</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  content</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> onCreateSendEmail</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#FFB86C;font-style:italic">                 snap</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> DocumentSnapshot</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#FFB86C;font-style:italic">                 _context</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> EventContext</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> demo</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Demo</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> snap.</span><span style="color:#50FA7B">data</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> Demo</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#6272A4">    // TODO: send email</span></span>
<span class="line"><span style="color:#F8F8F2">  } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(err);</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>Nodemailer</h3>
<p>In order to effectively send email, we are going to use <a href="https://nodemailer.com/" rel="noopener noreferrer external">Nodemailer</a>.</p>
<blockquote>
<p>Nodemailer is a module for Node.js applications to allow easy as cake email sending. The project got started back in 2010 when there was no sane option to send email messages, today it is the solution most Node.js users turn to by default.</p>
</blockquote>
<blockquote>
<p>Nodemailer is licensed under MIT license.</p>
</blockquote>
<p>As you can notice, Nodemailer is not just compatible with Firebase Cloud Functions but also with any <a href="https://nodejs.org/" rel="noopener noreferrer external">Node.js</a> projects.</p>
<p>To install it in our project, we run the following command:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> install</span><span style="color:#F1FA8C"> nodemailer</span><span style="color:#BD93F9"> --save</span></span></code></pre><p>Furthermore, we also install its typings definition.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> install</span><span style="color:#F1FA8C"> @types/nodemailer</span><span style="color:#BD93F9"> --save-dev</span></span></code></pre><hr />
<h4>SMTP Transport</h4>
<p>Nodemailer uses SMTP as the main transport to deliver messages. Therefore, your email delivery provider should support such protocol. It also supports either the LTS or STARTTLS extension. In this post we are going to use STARTTLS and therefore are going to set the flag <code>secure</code> to <code>false</code> to activate this protocol.</p>
<p>You can find all options in the library <a href="https://nodemailer.com/smtp/" rel="noopener noreferrer external">documentation</a>.</p>
<hr />
<h3>Configuration</h3>
<p>Specially if your project is open source, you might be interested to not hardcode your SMTP login, password, and host in your code but rather hide these in a configuration.</p>
<p>Firebase offers such ability. We can create a script to <code>set</code> these.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">#!/bin/sh</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">firebase</span><span style="color:#F1FA8C"> functions:config:set</span><span style="color:#F1FA8C"> mail.from=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">hello@domain.com</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C"> mail.pwd=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">password</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C"> mail.to=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">david@domain.com</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C"> mail.host=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">mail.provider.com</span><span style="color:#E9F284">"</span></span></code></pre><p>To retrieve the configuration in our function, we can access the configuration through <code>functions.config()</code> followed by the keys we just defined above.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> mailFrom</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> functions.</span><span style="color:#50FA7B">config</span><span style="color:#F8F8F2">().mail.from;</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> mailPwd</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> functions.</span><span style="color:#50FA7B">config</span><span style="color:#F8F8F2">().mail.pwd;</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> mailTo</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> functions.</span><span style="color:#50FA7B">config</span><span style="color:#F8F8F2">().mail.to;</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> mailHost</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> functions.</span><span style="color:#50FA7B">config</span><span style="color:#F8F8F2">().mail.host;</span></span></code></pre><hr />
<h3>Send Email</h3>
<p>We have the transport, we have the configuration, we just need the final piece: the message.</p>
<p>I rather like to send my self HTML email, allowing me to include links in the<br />
content, that’s why here too, we are using such a format.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> mailOptions </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    from</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> mailFrom,</span></span>
<span class="line"><span style="color:#F8F8F2">    to</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> mailTo,</span></span>
<span class="line"><span style="color:#F8F8F2">    subject</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Hello World</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    html</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> `&#x3C;p></span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">demo.content</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">&#x3C;/p>`</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Finally, we can use Nodemailer to create the channel and ultimately send our email.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> transporter</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Mail</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> nodemailer.</span><span style="color:#50FA7B">createTransport</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  host</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> mailHost,</span></span>
<span class="line"><span style="color:#F8F8F2">  port</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 587</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  secure</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">, </span><span style="color:#6272A4">// STARTTLS</span></span>
<span class="line"><span style="color:#F8F8F2">  auth</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    type</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">LOGIN</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    user</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> mailFrom,</span></span>
<span class="line"><span style="color:#F8F8F2">    pass</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> mailPwd</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">await</span><span style="color:#F8F8F2"> transporter.</span><span style="color:#50FA7B">sendMail</span><span style="color:#F8F8F2">(mailOptions);</span></span></code></pre><hr />
<h3>Altogether</h3>
<p>All in all, our function is the following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#BD93F9"> *</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> functions </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">firebase-functions</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { EventContext } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">firebase-functions</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { DocumentSnapshot } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">firebase-functions/lib/providers/firestore</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#BD93F9"> *</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> Mail </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">nodemailer/lib/mailer</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#BD93F9"> *</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> nodemailer </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">nodemailer</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> watchCreate </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#F8F8F2">       functions.</span></span>
<span class="line"><span style="color:#F8F8F2">       firestore.</span></span>
<span class="line"><span style="color:#50FA7B">       document</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">demo/{demoId}</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">).</span><span style="color:#50FA7B">onCreate</span><span style="color:#F8F8F2">(onCreateSendEmail);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">interface</span><span style="color:#8BE9FD;font-style:italic"> Demo</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  content</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> onCreateSendEmail</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#FFB86C;font-style:italic">                 snap</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> DocumentSnapshot</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#FFB86C;font-style:italic">                 _context</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> EventContext</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> demo</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Demo</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> snap.</span><span style="color:#50FA7B">data</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> Demo</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> mailFrom</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> functions.</span><span style="color:#50FA7B">config</span><span style="color:#F8F8F2">().info.mail.from;</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> mailPwd</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> functions.</span><span style="color:#50FA7B">config</span><span style="color:#F8F8F2">().info.mail.pwd;</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> mailTo</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> functions.</span><span style="color:#50FA7B">config</span><span style="color:#F8F8F2">().info.mail.to;</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> mailHost</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> functions.</span><span style="color:#50FA7B">config</span><span style="color:#F8F8F2">().info.mail.host;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> mailOptions </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">      from</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> mailFrom,</span></span>
<span class="line"><span style="color:#F8F8F2">      to</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> mailTo,</span></span>
<span class="line"><span style="color:#F8F8F2">      subject</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">Hello World</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">      html</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> `&#x3C;p></span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">demo.content</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">&#x3C;/p>`</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> transporter</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Mail</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> nodemailer.</span><span style="color:#50FA7B">createTransport</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">      host</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> mailHost,</span></span>
<span class="line"><span style="color:#F8F8F2">      port</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 587</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">      secure</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">, </span><span style="color:#6272A4">// STARTTLS</span></span>
<span class="line"><span style="color:#F8F8F2">      auth</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        type</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">LOGIN</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        user</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> mailFrom,</span></span>
<span class="line"><span style="color:#F8F8F2">        pass</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> mailPwd</span></span>
<span class="line"><span style="color:#F8F8F2">      }</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#F8F8F2"> transporter.</span><span style="color:#50FA7B">sendMail</span><span style="color:#F8F8F2">(mailOptions);</span></span>
<span class="line"><span style="color:#F8F8F2">  } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(err);</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>Summary</h3>
<p>With the help of a Firebase and Nodemailer, it is possible to relatively quickly set up a function which triggers email. I hope this introduction gave you some hints on how to implement such a feature and that you are going to give a try to <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a> for your next presentations.</p>
<p>I am looking forward to receiving an email telling me that I have to check your published slides 😉.</p>
<p>To infinity and beyond!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Re-implementing document.execCommand()]]></title>
          <description><![CDATA[The execCommand method has been marked has obsolete, here is how I re-implemented its styling command.]]></description>
          <link>https://daviddalbusco.com/blog/reimplementing-document-execcommand/</link>
          <pubDate>Thu, 13 Aug 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*BDpmr6_txiIdod6OCOyvZA.jpeg" alt="" / loading="lazy"></p>
<p>*Photo by <a href="https://unsplash.com/@nathanrodrigue_z?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Nathan Rodriguez</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></span></p>
<blockquote>
<p>This feature is obsolete. Although it may still work in some browsers, its use is discouraged since it could be removed at any time. Try to avoid using it. — MDN web docs</p>
</blockquote>
<p>Without a clear explanation on why nor when, <code>document.execCommand()</code> has been marked as obsolete in the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand" rel="noopener noreferrer external">MDN web docs</a>. Fun fact, it is not marked as deprecated in all languages, as for example French or Spanish which do not mention anything 😜.</p>
<p>For <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>, an open source web editor for slides, we have developed and published a custom <a href="https://docs.deckdeckgo.com/components/inline-editor" rel="noopener noreferrer external">WYSIWYG editor</a> which relied on such feature.</p>
<p>Because it may be future proof to proactively replace its usage by a custom implementation, I spent quite some time reimplementing it 😄.</p>
<p>Even though my implementation does not look that bad (I hope), I kind of feel, I had to re-implement the wheel. That’s why I am sharing with you my solution, hoping that some of you might point out some improvements or even better, send us <a href="https://github.com/deckgo/deckdeckgo/" rel="noopener noreferrer external">pull requests</a> to make the component rock solid 🙏.</p>
<hr />
<h3>Introduction</h3>
<p>One thing I like about our WYSIWYG editor is its cross devices compatibility. It works on desktop as on mobile devices where, instead of appearing as a floating popup, it will be attached either at the top (iOS) or bottom of the viewport (Android) according how the keyboard behaves.</p>
<p>It can change text style (bold, italic, underline and strikethrough), fore- and background color, alignment (left, center or right), lists (ordered and not ordered) and even exposes a <code>slot</code> for a custom action.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*4wuYSS_u3KfKHSI1xLdR4g.gif" alt="" / loading="lazy"></p>
<hr />
<h3>Limitation</h3>
<p>My following re-implementation of <code>document.execCommand</code> do seems to work well but, it does not support an undo functionality (yet), what’s still a bummer 😕.</p>
<p>As for the code itself, I am open to any suggestions, ping me with your best ideas!</p>
<hr />
<h3>Goal</h3>
<p>The objective shared in the blog post is the re-implementation of following functions (source <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand" rel="noopener noreferrer external">MDN web docs</a>):</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">document.</span><span style="color:#50FA7B">execCommand</span><span style="color:#F8F8F2">(aCommandName, aShowDefaultUI, aValueArgument);</span></span></code></pre><ul>
<li><strong>bold</strong>: Toggles bold on/off for the selection or at the insertion point.</li>
<li><strong>italic</strong>: Toggles italics on/off for the selection or at the insertion point.</li>
<li>**underline: **Toggles underline on/off for the selection or at the insertion point.</li>
<li><strong>strikeThrough</strong>: Toggles strikethrough on/off for the selection or at the insertion point.</li>
<li><strong>foreColor</strong>: Changes a font color for the selection or at the insertion point. This requires a hexadecimal color value string as a value argument.</li>
<li><strong>backColor</strong>: Changes the document background color.</li>
</ul>
<hr />
<h3>Implementation</h3>
<p>I feel more comfortable using <a href="https://www.typescriptlang.org/" rel="noopener noreferrer external">TypeScript</a> when I develop, well, anything JavaScript related, that’s why the following code is type and why I also began the implementation by declaring an interface for the actions.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> interface</span><span style="color:#8BE9FD;font-style:italic"> ExecCommandStyle</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  style</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">color</span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> |</span></span>
<span class="line"><span style="color:#E9F284">         '</span><span style="color:#F1FA8C">background-color</span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> |</span></span>
<span class="line"><span style="color:#E9F284">         '</span><span style="color:#F1FA8C">font-size</span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> |</span></span>
<span class="line"><span style="color:#E9F284">         '</span><span style="color:#F1FA8C">font-weight</span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> |</span></span>
<span class="line"><span style="color:#E9F284">         '</span><span style="color:#F1FA8C">font-style</span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> |</span></span>
<span class="line"><span style="color:#E9F284">         '</span><span style="color:#F1FA8C">text-decoration</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#50FA7B">  initial</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">element</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLElement</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">boolean</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Instead of trying to create new elements as the actual API does per default, I decided that it should instead modifies CSS attributes. The <code>value</code> can take for example the value <code>bold</code> if the style is <code>font-weight</code> or <code>#ccc</code> if a color is applied. The interface also contains a function <code>initial</code> which I am going to use to determine is a style should be applied or removed.</p>
<hr />
<p>Once the interface declared, I began the implementation of the function will take cares of applying the style. It begin by capturing the user selected text, the <code>selection</code> , and identifying its <code>container</code> . Interesting to notice that the container can either be the text itself or the parent element of the selection.</p>
<p>It is also worth to notice that the function takes a second parameter <code>containers</code> which defines a list of elements in which the function can be applied. Per default <code>h1,h2,h3,h4,h5,h6,div</code> . I introduced this limitation to not iterate through the all DOM when searching for information.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> execCommandStyle</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#FFB86C;font-style:italic">                 action</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> ExecCommandStyle</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">containers</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> selection</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Selection</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> getSelection</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">selection) {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> anchorNode</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Node</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> selection.anchorNode;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">anchorNode) {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> container</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLElement</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#F8F8F2">    anchorNode.nodeType </span><span style="color:#FF79C6">!==</span><span style="color:#F8F8F2"> Node.</span><span style="color:#BD93F9">TEXT_NODE</span></span>
<span class="line"><span style="color:#FF79C6">    &#x26;&#x26;</span><span style="color:#F8F8F2"> anchorNode.nodeType </span><span style="color:#FF79C6">!==</span><span style="color:#F8F8F2"> Node.</span><span style="color:#BD93F9">COMMENT_NODE</span><span style="color:#FF79C6"> ?</span></span>
<span class="line"><span style="color:#F8F8F2">      (anchorNode </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> HTMLElement</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> anchorNode.parentElement;</span></span>
<span class="line"><span style="color:#6272A4">  // TODO: next chapter</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> getSelection</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Selection</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> (window </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> window.getSelection) {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> window.</span><span style="color:#50FA7B">getSelection</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  } </span><span style="color:#FF79C6">else</span><span style="color:#FF79C6"> if</span><span style="color:#F8F8F2"> (document </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> document.getSelection) {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> document.</span><span style="color:#50FA7B">getSelection</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  } </span><span style="color:#FF79C6">else</span><span style="color:#FF79C6"> if</span><span style="color:#F8F8F2"> (document </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> (document </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> any</span><span style="color:#F8F8F2">).selection) {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> (document </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> any</span><span style="color:#F8F8F2">).selection.</span><span style="color:#50FA7B">createRange</span><span style="color:#F8F8F2">().text;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#BD93F9"> null</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<p>The idea is to style the text with CSS attributes. That's why I am going to convert the user's selection into <code>span</code>.</p>
<p>Even though, I thought that it would be better to not always add new elements to the DOM. For example, if a user select a background color red and then green for the exact same selection, it is probably better to modify the existing style rather than adding a <code>span</code> child to another <code>span</code> with both the same CSS attributes. That’s why I implemented a text based comparison to either <code>updateSelection</code> or <code>replaceSelection</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> sameSelection</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#F8F8F2">      container </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> container.innerText </span><span style="color:#FF79C6">===</span><span style="color:#F8F8F2"> selection.</span><span style="color:#50FA7B">toString</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">if</span><span style="color:#F8F8F2"> (sameSelection </span><span style="color:#FF79C6">&#x26;&#x26;</span></span>
<span class="line"><span style="color:#FF79C6">  !</span><span style="color:#50FA7B">isContainer</span><span style="color:#F8F8F2">(containers, container)</span></span>
<span class="line"><span style="color:#FF79C6">  &#x26;&#x26;</span><span style="color:#F8F8F2"> container.style[action.style] </span><span style="color:#FF79C6">!==</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#50FA7B"> updateSelection</span><span style="color:#F8F8F2">(container, action, containers);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">await</span><span style="color:#50FA7B"> replaceSelection</span><span style="color:#F8F8F2">(container, action, selection, containers);</span></span></code></pre><hr />
<h4>Update Selection</h4>
<p>By updating the selection, I mean applying the new style to an existing element. For example transforming <code>&lt;span style=&quot;background-color: red;&quot;/&gt;</code> to <code>&lt;span style=&quot;background-color: green;&quot;/&gt;</code> because the user selected a new background color.</p>
<p>Furthermore, when user applies a selection, I noticed, as for example with MS Word, that the children should inherit the new selection. That’s why after having applied the style, I created another function to clean the style of the children.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> updateSelection</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">container</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLElement</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">action</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> ExecCommandStyle</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">containers</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">  container.style[action.style] </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#FF79C6">            await</span><span style="color:#50FA7B"> getStyleValue</span><span style="color:#F8F8F2">(container, action, containers);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  await</span><span style="color:#50FA7B"> cleanChildren</span><span style="color:#F8F8F2">(action, container);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Applying the style needs a bit more work than setting a new value. Indeed, as for example with <code>bold</code> or <code>italic</code> , the user might want to apply it, then remove it, then apply it again, then remove it again etc.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> getStyleValue</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">container</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLElement</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">action</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> ExecCommandStyle</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">containers</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">container) {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> action.value;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">await</span><span style="color:#F8F8F2"> action.</span><span style="color:#50FA7B">initial</span><span style="color:#F8F8F2">(container)) {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">initial</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> style</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Node</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#FF79C6">        await</span><span style="color:#50FA7B"> findStyleNode</span><span style="color:#F8F8F2">(container, action.style, containers);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">await</span><span style="color:#F8F8F2"> action.</span><span style="color:#50FA7B">initial</span><span style="color:#F8F8F2">(style </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> HTMLElement</span><span style="color:#F8F8F2">)) {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">initial</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#F8F8F2"> action.value;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>In case of <code>bold</code> , the <code>initial</code> function is a simple check on the attribute.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#F8F8F2">  style: </span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">font-weight</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  value: </span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">bold</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  initial: (</span><span style="color:#FFB86C;font-style:italic">element</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLElement</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">           Promise</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">resolve</span><span style="color:#F8F8F2">(element </span><span style="color:#FF79C6">&#x26;&#x26;</span></span>
<span class="line"><span style="color:#F8F8F2">                           element.style[</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">font-weight</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">] </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">bold</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>When it comes to color, it becomes a bit more tricky as the value can either be an <code>hex</code> or a <code>rgb</code> value. That’s why I had to check both.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#F8F8F2">  style: </span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.action,</span></span>
<span class="line"><span style="color:#F8F8F2">  value: $event.detail.hex, </span><span style="color:#6272A4">// The result of our color picker</span></span>
<span class="line"><span style="color:#F8F8F2">  initial: (</span><span style="color:#FFB86C;font-style:italic">element</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLElement</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">boolean</span><span style="color:#F8F8F2">>(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">      const</span><span style="color:#F8F8F2"> rgb</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> hexToRgb</span><span style="color:#F8F8F2">($event.detail.hex);</span></span>
<span class="line"><span style="color:#50FA7B">      resolve</span><span style="color:#F8F8F2">(element </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> (element.style[</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.action] </span><span style="color:#FF79C6">===</span></span>
<span class="line"><span style="color:#F8F8F2">              $event.detail.hex </span><span style="color:#FF79C6">||</span></span>
<span class="line"><span style="color:#F8F8F2">              element.style[</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.action] </span><span style="color:#FF79C6">===</span><span style="color:#F1FA8C"> `rgb(</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">rgb</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">)`</span><span style="color:#F8F8F2">));</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>With the help of such definition, I can check if style should be added or removed respectively set to <code>initial</code> .</p>
<p>Unfortunately, it is not enough. The container might inherit its style from a parent as for example <code>&lt;div style=&quot;font-weight: bold&quot;&gt;&lt;span/&gt;&lt;/div&gt;</code> . That’s why I created the method <code>findStyleNode</code> which recursively iterates till it either find an element with the same style or the container.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> findStyleNode</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">node</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Node</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#FFB86C;font-style:italic">                        style</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#FFB86C;font-style:italic">                        containers</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Node</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#6272A4">  // Just in case</span></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> (node.nodeName.</span><span style="color:#50FA7B">toUpperCase</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">HTML</span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> ||</span></span>
<span class="line"><span style="color:#F8F8F2">     node.nodeName.</span><span style="color:#50FA7B">toUpperCase</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">BODY</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9"> null</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">node.parentNode) {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9"> null</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> (DeckdeckgoInlineEditorUtils.</span><span style="color:#50FA7B">isContainer</span><span style="color:#F8F8F2">(containers, node)) {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9"> null</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> hasStyle</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#F8F8F2">    (node </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> HTMLElement</span><span style="color:#F8F8F2">).style[style] </span><span style="color:#FF79C6">!==</span><span style="color:#BD93F9"> null</span><span style="color:#FF79C6"> &#x26;&#x26;</span></span>
<span class="line"><span style="color:#F8F8F2">    (node </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> HTMLElement</span><span style="color:#F8F8F2">).style[style] </span><span style="color:#FF79C6">!==</span><span style="color:#BD93F9"> undefined</span><span style="color:#FF79C6"> &#x26;&#x26;</span></span>
<span class="line"><span style="color:#F8F8F2">    (node </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> HTMLElement</span><span style="color:#F8F8F2">).style[style] </span><span style="color:#FF79C6">!==</span><span style="color:#E9F284"> ''</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> (hasStyle) {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> node;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> findStyleNode</span><span style="color:#F8F8F2">(node.parentNode, style, containers);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Finally, the style can be applied and <code>cleanChildren</code> can be executed. It is also a recursive method but instead of iterating to the top of the DOM tree, in iterates to the bottom of the container until it has processed all children.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> cleanChildren</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">action</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> ExecCommandStyle</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#FFB86C;font-style:italic">                             span</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLSpanElement</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">span.</span><span style="color:#50FA7B">hasChildNodes</span><span style="color:#F8F8F2">()) {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">  // Clean direct (> *) children with same style</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> children</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLElement</span><span style="color:#F8F8F2">[] </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#F8F8F2">        Array.</span><span style="color:#50FA7B">from</span><span style="color:#F8F8F2">(span.children)</span></span>
<span class="line"><span style="color:#F8F8F2">             .</span><span style="color:#50FA7B">filter</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">element</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLElement</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">                return</span><span style="color:#F8F8F2"> element.style[action.style] </span><span style="color:#FF79C6">!==</span><span style="color:#BD93F9"> undefined</span><span style="color:#FF79C6"> &#x26;&#x26;</span></span>
<span class="line"><span style="color:#F8F8F2">                       element.style[action.style] </span><span style="color:#FF79C6">!==</span><span style="color:#E9F284"> ''</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">              }) </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> HTMLElement</span><span style="color:#F8F8F2">[];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> (children </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> children.length </span><span style="color:#FF79C6">></span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">    children.</span><span style="color:#50FA7B">forEach</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">element</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLElement</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">      element.style[action.style] </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> ''</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">      if</span><span style="color:#F8F8F2"> (element.</span><span style="color:#50FA7B">getAttribute</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">style</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> ''</span><span style="color:#FF79C6"> ||</span></span>
<span class="line"><span style="color:#F8F8F2">          element.style </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> null</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">        element.</span><span style="color:#50FA7B">removeAttribute</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">style</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">      }</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">  // Direct children (> *) may have children (*) to be clean too</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> cleanChildrenChildren</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">void</span><span style="color:#F8F8F2">>[] </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#F8F8F2">    Array.</span><span style="color:#50FA7B">from</span><span style="color:#F8F8F2">(span.children).</span><span style="color:#50FA7B">map</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">element</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLElement</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">      return</span><span style="color:#50FA7B"> cleanChildren</span><span style="color:#F8F8F2">(action, element);</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">cleanChildrenChildren </span><span style="color:#FF79C6">||</span><span style="color:#F8F8F2"> cleanChildrenChildren.length </span><span style="color:#FF79C6">&#x3C;=</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  await</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">all</span><span style="color:#F8F8F2">(cleanChildrenChildren);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h4>Replace Selection</h4>
<p>Replacing a selection to apply a style is a bit less verbose fortunately. With the help of a range, I extract a fragment which can be added as content of new <code>span</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> replaceSelection</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">container</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLElement</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#FFB86C;font-style:italic">                                action</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> ExecCommandStyle</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#FFB86C;font-style:italic">                                selection</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Selection</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#FFB86C;font-style:italic">                                containers</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> range</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Range</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> selection.</span><span style="color:#50FA7B">getRangeAt</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> fragment</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> DocumentFragment</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> range.</span><span style="color:#50FA7B">extractContents</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> span</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLSpanElement</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#FF79C6">              await</span><span style="color:#50FA7B"> createSpan</span><span style="color:#F8F8F2">(container, action, containers);</span></span>
<span class="line"><span style="color:#F8F8F2">  span.</span><span style="color:#50FA7B">appendChild</span><span style="color:#F8F8F2">(fragment);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  await</span><span style="color:#50FA7B"> cleanChildren</span><span style="color:#F8F8F2">(action, span);</span></span>
<span class="line"><span style="color:#FF79C6">  await</span><span style="color:#50FA7B"> flattenChildren</span><span style="color:#F8F8F2">(action, span);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  range.</span><span style="color:#50FA7B">insertNode</span><span style="color:#F8F8F2">(span);</span></span>
<span class="line"><span style="color:#F8F8F2">  selection.</span><span style="color:#50FA7B">selectAllChildren</span><span style="color:#F8F8F2">(span);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>To apply the style to the new <code>span</code> , fortunately, I can reuse the function <code>getStyleValue</code> as already introduced in the previous chapter.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> createSpan</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">container</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLElement</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#FFB86C;font-style:italic">                     action</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> ExecCommandStyle</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#FFB86C;font-style:italic">                     containers</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">HTMLSpanElement</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> span</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLSpanElement</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> document.</span><span style="color:#50FA7B">createElement</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">span</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">  span.style[action.style] </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#FF79C6">            await</span><span style="color:#50FA7B"> getStyleValue</span><span style="color:#F8F8F2">(container, action, containers);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#F8F8F2"> span;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Likewise, once the new <code>span</code> is created, and the fragment applied, I have to <code>cleanChildren</code> to apply the new style to all descendants. Fortunately, again, that function is the same as the one introduced in the previous chapter.</p>
<p>Finally, because I am looking to avoid <code>span</code> elements without style, I created a function <code>flattenChildren</code> which aims to find children of the new style and which, after having been cleaned, do not contain any styles at all anymore. If I find such elements, I convert these back to text node.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> flattenChildren</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">action</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> ExecCommandStyle</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#FFB86C;font-style:italic">                               span</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLSpanElement</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">span.</span><span style="color:#50FA7B">hasChildNodes</span><span style="color:#F8F8F2">()) {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">  // Flatten direct (> *) children with no style</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> children</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLElement</span><span style="color:#F8F8F2">[] </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#F8F8F2">      Array.</span><span style="color:#50FA7B">from</span><span style="color:#F8F8F2">(span.children).</span><span style="color:#50FA7B">filter</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">element</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLElement</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">         const</span><span style="color:#F8F8F2"> style</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> element.</span><span style="color:#50FA7B">getAttribute</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">style</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">         return</span><span style="color:#FF79C6"> !</span><span style="color:#F8F8F2">style </span><span style="color:#FF79C6">||</span><span style="color:#F8F8F2"> style </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> ''</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">      }) </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> HTMLElement</span><span style="color:#F8F8F2">[];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> (children </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> children.length </span><span style="color:#FF79C6">></span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">    children.</span><span style="color:#50FA7B">forEach</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">element</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLElement</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">      const</span><span style="color:#F8F8F2"> styledChildren</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> NodeListOf</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">HTMLElement</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#F8F8F2">            element.</span><span style="color:#50FA7B">querySelectorAll</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">[style]</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">      if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">styledChildren </span><span style="color:#FF79C6">||</span><span style="color:#F8F8F2"> styledChildren.length </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> text</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Text</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#F8F8F2">              document.</span><span style="color:#50FA7B">createTextNode</span><span style="color:#F8F8F2">(element.textContent);</span></span>
<span class="line"><span style="color:#F8F8F2">        element.parentElement.</span><span style="color:#50FA7B">replaceChild</span><span style="color:#F8F8F2">(text, element);</span></span>
<span class="line"><span style="color:#F8F8F2">      }</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">  // Direct children (> *) may have children (*) to flatten too</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> flattenChildrenChildren</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">void</span><span style="color:#F8F8F2">>[] </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#F8F8F2">    Array.</span><span style="color:#50FA7B">from</span><span style="color:#F8F8F2">(span.children).</span><span style="color:#50FA7B">map</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">element</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLElement</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">       return</span><span style="color:#50FA7B"> flattenChildren</span><span style="color:#F8F8F2">(action, element);</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">flattenChildrenChildren </span><span style="color:#FF79C6">||</span></span>
<span class="line"><span style="color:#F8F8F2">      flattenChildrenChildren.length </span><span style="color:#FF79C6">&#x3C;=</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  await</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">all</span><span style="color:#F8F8F2">(flattenChildrenChildren);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>Altogether</h3>
<p>You can find the all code introduced in this blog post in our repo, more precisely:</p>
<ul>
<li>the WYSIWYG <a href="https://github.com/deckgo/deckdeckgo/tree/master/webcomponents/inline-editor" rel="noopener noreferrer external">Web Component</a></li>
<li>the ExecCommandStyle <a href="https://github.com/deckgo/deckdeckgo/blob/1978c754c0955dba9077a75d4d6d48eea50bdf99/webcomponents/inline-editor/src/interfaces/interfaces.ts#L17" rel="noopener noreferrer external">interface</a></li>
<li>the implementation of the <a href="https://github.com/deckgo/deckdeckgo/blob/master/webcomponents/inline-editor/src/utils/execcommand-style.utils.tsx" rel="noopener noreferrer external">function</a></li>
</ul>
<p>If you are looking to try it out locally, you will need to clone our <a href="https://github.com/deckgo/deckdeckgo" rel="noopener noreferrer external">mono-repo</a>.</p>
<hr />
<h3>Conclusion</h3>
<p>As I am reaching the conclusion of this blog post, looking back at it once again, I am honestly not sure anyone will ever understand my explanations 😅. I hope that at least it has aroused your curiosity for our WYSIWYG component and generally speaking, for our editor.</p>
<p>Give a try to <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a> to compose your next slides and ping us with your best ideas and feedbacks afterwards 😁.</p>
<p>To infinity and beyond!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Ionic: Fullscreen Modal & Menu Popover]]></title>
          <description><![CDATA[How to make Ionic modals become fullscreen and popovers act as menu]]></description>
          <link>https://daviddalbusco.com/blog/ionic-fullscreen-modal-menu-popover/</link>
          <pubDate>Tue, 04 Aug 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*v93pK6K7hTbHnVI3dqzhyA.jpeg" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@dinoreichmuth?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Dino Reichmuth</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p><a href="https://ionicframework.com" rel="noopener noreferrer external">Ionic</a> is not just an amazing design system made for mobile devices but also work like a charm on desktop. We use it at <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a> particularly for that reason.</p>
<p>While we developed our editor for presentations, we implemented the following two tricks I am about to share with you and which I hope, may be one day useful to you too.</p>
<hr />
<h3>Fullscreen Modal</h3>
<p>Out of the box, regardless which platform’s style is applied, as soon as your browser reaches the size of 768x600 pixels, an Ionic modal will not be displayed fullscreen anymore but rather as a centered popup.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*iFK9OeqLfVNQPERmhKMDww.gif" alt="" / loading="lazy"></p>
<p>While that might fits almost all the use cases, there might be one which would request a full screen modal. Indeed, you might want to present multiple information to the user, therefore need space, but might not want to add a page in the history stack.</p>
<p>For example, users editing their slides with our editor are able to ship their decks as Progressive Web Apps. Upon request, we are packaging their presentations in PWAs and are deploying these online. As it is quite a process and something which the user has to explicitly trigger, we have decided to present all the information in a modal rather than using a navigation, avoiding possible back and forth errors 😇.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*QteGvwweJXOOrG63Qid7gA.gif" alt="" / loading="lazy"></p>
<hr />
<p>Assuming you are not looking to make all your modals fullscreen but only some, I suggest that we style the modals with the help of a CSS class selector which we can apply as displayed on the <a href="https://ionicframework.com/docs/api/modal" rel="noopener noreferrer external">documentation</a>.</p>
<p>Angular:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">async </span><span style="color:#50FA7B">presentModal</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> modal </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.modalController.</span><span style="color:#50FA7B">create</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    component</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> ModalPage,</span></span>
<span class="line"><span style="color:#F8F8F2">    cssClass</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">fullscreen</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"><span style="color:#FF79C6">  await</span><span style="color:#F8F8F2"> modal.</span><span style="color:#50FA7B">present</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Stencil:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">async </span><span style="color:#50FA7B">presentModal</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> modal </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> modalController.</span><span style="color:#50FA7B">create</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    component</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-page-modal</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    cssClass</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">fullscreen</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"><span style="color:#FF79C6">  await</span><span style="color:#F8F8F2"> modal.</span><span style="color:#50FA7B">present</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>React:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonModal</span><span style="color:#50FA7B;font-style:italic"> isOpen</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">showModal</span><span style="color:#FF79C6">}</span><span style="color:#50FA7B;font-style:italic"> cssClass</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">fullscreen</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">PageModal</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">PageModal</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonModal</span><span style="color:#F8F8F2">></span></span></code></pre><p>Vue:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">import Modal from './modal.vue'</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">export default </span><span style="color:#FF79C6">{</span></span>
<span class="line"><span style="color:#F8F8F2">  methods: {</span></span>
<span class="line"><span style="color:#50FA7B">    openModal</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">      return</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.$ionic.modalController</span></span>
<span class="line"><span style="color:#F8F8F2">        .</span><span style="color:#50FA7B">create</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">          component</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> Modal,</span></span>
<span class="line"><span style="color:#F8F8F2">          cssClass</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">fullscreen</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">        })</span></span>
<span class="line"><span style="color:#F8F8F2">        .</span><span style="color:#50FA7B">then</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">m</span><span style="color:#FF79C6"> =></span><span style="color:#F8F8F2"> m.</span><span style="color:#50FA7B">present</span><span style="color:#F8F8F2">())</span></span>
<span class="line"><span style="color:#F8F8F2">    },</span></span>
<span class="line"><span style="color:#F8F8F2">  },</span></span>
<span class="line"><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span></code></pre><hr />
<p>The style, <code>.fullscreen</code>, should be defined on the application level and not higher in the hierarchy than <code>ion-app</code>, because the modals are going to be injected in the DOM in <code>ion-modal</code> elements which are direct descendant of the app container. For example, in our Stencil application I defined it in <code>app.css</code> or, in a React one, I define it in a stylesheet I import in <code>App.tsx</code> .</p>
<p>It should contain the information to apply a full screen sizing (width/height) and indication that the modal should not be displayed with rounded corner.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">ion-modal</span><span style="color:#50FA7B;font-style:italic">.fullscreen</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    --width</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 100</span><span style="color:#FF79C6">%</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    --height</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 100</span><span style="color:#FF79C6">%</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    --border-radius</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>That’s it, nothing more, nothing less 😄.</p>
<hr />
<h3>Popover Menu</h3>
<p>Not sure anyone else would actually have the following requirement, but you might need, as we did in DeckDeckGo, a menu which is not tied to the navigation respectively which is not the <code>ion-menu</code> .</p>
<p>For example, we had to find a solution to display options without hiding all the content when our users were editing their slides. Even though we could have developed a custom popup for such purpose, we thought that reusing the Ionic popover would be nice. I mean, look at that sweat animation triggered on opening 🤩.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*m7NWUeN_E-Rdw78SKuDrzA.gif" alt="" / loading="lazy"></p>
<hr />
<p>As in previous chapter about the modal, I am assuming that we might want to only apply this effect on specific popovers of our application, that’s why we are again going to use a CSS style class. Moreover, we also want to explicitly use the mode <code>md</code> to give the popover a “flat” style and avoid the display of a backdrop. This last point is not mandatory but make sense if you want your user to be still able to see clearly what’s next to the “popover menu”.</p>
<p>Angular:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">async </span><span style="color:#50FA7B">presentPopover</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> popover </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.popoverController.</span><span style="color:#50FA7B">create</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    component</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> PopoverPage,</span></span>
<span class="line"><span style="color:#F8F8F2">    cssClass</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">menu</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    mode</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">md</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    showBackdrop</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> false</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"><span style="color:#FF79C6">  await</span><span style="color:#F8F8F2"> popover.</span><span style="color:#50FA7B">present</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Stencil:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">async </span><span style="color:#50FA7B">presentPopover</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> popover </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> popoverController.</span><span style="color:#50FA7B">create</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    component</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-page-popover</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    cssClass</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">menu</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    mode</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">md</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    showBackdrop</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> false</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"><span style="color:#FF79C6">  await</span><span style="color:#F8F8F2"> popover.</span><span style="color:#50FA7B">present</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>React:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonPopover</span><span style="color:#50FA7B;font-style:italic"> isOpen</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">showPopover</span><span style="color:#FF79C6">}</span><span style="color:#50FA7B;font-style:italic"> cssClass</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">menu</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> mode</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">md</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> showBackdrop</span><span style="color:#FF79C6">={</span><span style="color:#BD93F9">false</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">PagePopover</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">PagePopover</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonPopover</span><span style="color:#F8F8F2">></span></span></code></pre><p>Vue:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">import Popover from './popover.vue'</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">export default </span><span style="color:#FF79C6">{</span></span>
<span class="line"><span style="color:#F8F8F2">  methods: {</span></span>
<span class="line"><span style="color:#50FA7B">    openPopover</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">      return</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.$ionic.popoverController</span></span>
<span class="line"><span style="color:#F8F8F2">        .</span><span style="color:#50FA7B">create</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">          component</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> Popover,</span></span>
<span class="line"><span style="color:#F8F8F2">          cssClass</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">menu</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">          mode</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">md</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">          showBackdrop</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span></span>
<span class="line"><span style="color:#F8F8F2">        })</span></span>
<span class="line"><span style="color:#F8F8F2">        .</span><span style="color:#50FA7B">then</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">m</span><span style="color:#FF79C6"> =></span><span style="color:#F8F8F2"> m.</span><span style="color:#50FA7B">present</span><span style="color:#F8F8F2">())</span></span>
<span class="line"><span style="color:#F8F8F2">    },</span></span>
<span class="line"><span style="color:#F8F8F2">  },</span></span>
<span class="line"><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span></code></pre><hr />
<p>We define the style on the root level of the application because the <code>ion-popover</code> elements are added as direct children of the main <code>ion-app</code> element.</p>
<p>We set a width, for example <code>540px</code>, and a maximal value because we want to fit smaller devices too.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">ion-popover</span><span style="color:#50FA7B;font-style:italic">.menu</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    --width</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 540</span><span style="color:#FF79C6">px</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    --max-width</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 100</span><span style="color:#FF79C6">%</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Thanks to this definition, we were able to change the width of the popover, but we not yet able to set the correct position, the right side of the screen, and specify a height which covers the all window.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*YE9aSnlYYPZBceTmSe0eDA.gif" alt="" / loading="lazy"></p>
<p>Even though we are going to achieve our goal, I have to say that unfortunately, it is only possible with the help of the infamous <code>!important</code> selector. I opened a <a href="https://github.com/ionic-team/ionic-framework/issues/15036" rel="noopener noreferrer external">feature request</a> about it a while ago and it is one of these suggestions which is so rarely used, that the solution should come from the community, what makes sense to my eyes. Unfortunately too, I did not have time yet to provide a pull request, maybe someday.</p>
<p>Meanwhile, let’s use the following style. First of all, we set the popover to the top right and we also transform its origin to match that position too. Moreover, we set a default height to <code>100%</code> to match the screen and add a bit of strictly styling as defining a background and a light box-shadow.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">ion-popover</span><span style="color:#50FA7B;font-style:italic">.menu</span><span style="color:#FF79C6"> div</span><span style="color:#50FA7B;font-style:italic">.popover-content</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD">    top</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 0</span><span style="color:#FF79C6"> !important</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">    left</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> inherit</span><span style="color:#FF79C6"> !important</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">    right</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">    transform-origin</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> right</span><span style="color:#BD93F9"> top</span><span style="color:#FF79C6"> !important</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    --min-height</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 100</span><span style="color:#FF79C6">%</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">    background</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> white</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">    box-shadow</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> -8</span><span style="color:#FF79C6">px</span><span style="color:#BD93F9"> 0</span><span style="color:#BD93F9"> 16</span><span style="color:#FF79C6">px</span><span style="color:#8BE9FD"> rgba</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">0.12</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#8BE9FD">    border-radius</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>That’s it, our popover can act as a menu 😃.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*4djCKLd9CV0lWLmcGnH-YQ.gif" alt="" / loading="lazy"></p>
<hr />
<h3>Conclusion</h3>
<p>Give a try to <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a> for your next presentation and if you are up to improve these styles or have any other suggestion, please do collaborate to our project in <a href="https://github.com/deckgo/deckdeckgo/" rel="noopener noreferrer external">GitHub</a>, we are welcoming any contributions and idea.</p>
<p>To infinity and beyond!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Professional presentation building, tailored to your brand.]]></title>
          <description><![CDATA[Introducing another kind of editor for your company's presentations.]]></description>
          <link>https://daviddalbusco.com/blog/professional-presentation-building-tailored-to-your-brand/</link>
          <pubDate>Thu, 30 Jul 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*G1Y71S6H20wCW7WbgrQXew.jpeg" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@headwayio?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Headway</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<blockquote>
<p>Did you have a look at Bob’s presentation? He used the wrong logo, again.</p>
</blockquote>
<p>It's astonishing how much the look and feel of presentations vary, even within the same company. Templates, formatting, color palettes, fonts, logo – you name it, they are never the same across two decks. Presentations are rarely consistent, even when companies invest time and effort in brand identity and design guidelines.</p>
<p>Today we are thrilled to announce that <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a> is receiving support for templates for <a href="https://deckdeckgo.com/enterprise" rel="noopener noreferrer external">enterprise</a> customers. Create a template once and all presentations will then use the same.</p>
<hr />
<h3>Templates And Options That Match Your CI/CD</h3>
<p>With this new feature, we aim to provide you with a new kind of editor for presentations and to assist you in the implementation of your brand's CI/CD into your slides. In close collaboration with your designer and adhering to your guidelines, we will create a set of unbreakable templates to which you can apply your design, logo, colors, fonts, styles and any other specifications. These templates will then be used by all your employees and collaborators going forward, thereby streamlining your company's visual identity.</p>
<p>That's not all! Below are some other features we are rolling out for enterprise customers.</p>
<hr />
<h4>More Than Static Content</h4>
<p>Templates are made with the web and have no other limits than the web's own. It means that if your business offers web solutions, you can integrate these within your slides.</p>
<hr />
<h4>Collaborate And Share A Library</h4>
<p>Teams are nothing without good communication. That’s why multiple users can edit the same presentation in real time and share ideas and comments.</p>
<p>What's more, collaborators can now upload and use your company's media resources and assets in their own slide decks.</p>
<hr />
<h4>Always Up-To-Date</h4>
<p>Over time, your corporate style will evolve. We simplify this stylistic growth.</p>
<hr />
<h4>Custom Domain, In Housing and Share Privately</h4>
<p>You can publish your decks under your own domain name. It's also possible to create authenticated links for private content.</p>
<p>Finally, if you prefer to integrate DeckDeckGo into your infrastructure and private network instead of using our SaaS platform, we are here to help.</p>
<hr />
<h3>And So Much More</h3>
<p>That rounds up the new features for our enterprise customers - but we have already released a ton of things for everyone else!</p>
<hr />
<h4>Edit Anywhere, Showcase Everywhere</h4>
<p>The editor is available anywhere, it is a Progressive Web App!</p>
<p>Presentations work on computers, phones, tablets and more. They are published as standalone apps.</p>
<hr />
<h4>Polls</h4>
<p>Engage with your audience in real time by running polls within your slides. Get them involved during your presentations with their smartphones and show the results live.</p>
<hr />
<h4>Editing</h4>
<p>Syntax-highlighted code, math formulas, charts, YouTube, Unsplash, Tenor, Google Fonts. We integrated the most useful services directly in the editor and are providing many components to render information smoothly.</p>
<hr />
<h4>Remote control</h4>
<p>Control your presentations remotely, draw on your slides and set a timer from your phone or tablet.</p>
<hr />
<h4>Speaker notes</h4>
<p>Write speaker notes for your slides. Embed them in your presentation when and where needed.</p>
<hr />
<h4>Open Source</h4>
<p>DeckDeckGo is open source. All of the code from our applications and components is available on <a href="https://github.com/deckgo/deckdeckgo" rel="noopener noreferrer external">GitHub</a>.</p>
<p>We encourage companies to adopt this approach, but we do understand if you prefer to keep your corporate templates private.</p>
<hr />
<h4>Developer Kit</h4>
<p>All public features used by our editor are available as a developer kit. Therefore, if you would rather prepare your presentation with HTML or Markdown, go for it by using <code>npm init deckeckgo</code>.</p>
<hr />
<h3>Get In Touch</h3>
<p>I hope the above introduction (and giving <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a> a try for yourself) made you curious about our solutions for enterprise clients. Get in touch for a tailored discussion about your needs by sending us an <a href="mailto:hello@deckdeckgo.com" rel="noopener noreferrer external">email</a>.</p>
<p>To infinity and beyond!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[App Shortcuts And Maskable Icons: Play It Like Twitter]]></title>
          <description><![CDATA[Follow the Twitter example to add App Shortcuts and maskable icons to your Progressive Web Apps.]]></description>
          <link>https://daviddalbusco.com/blog/app-shortcuts-and-maskable-icons-play-it-like-twitter/</link>
          <pubDate>Thu, 16 Jul 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*GGBgZEgWp8_bR5S2fVhybg.jpeg" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@ravinepz?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Ravi Sharma</a> on <a href="https://unsplash.com/s/photos/twitter?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>We recently added App Shortcuts and maskable icons to <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>. While I was reading tutorials to implement these features, I came across some questions regarding design such as:</p>
<ul>
<li>What’s the size of the safe area?</li>
<li>Should the shortcuts’ icons colors be contrasting?</li>
<li>Shortcuts icons are maskable icons or regular ones?</li>
<li>Can both maskable and regular icons find place together?</li>
</ul>
<p>While I would have probably be able to solve these questions by my self, by reading more carefully the related blog posts or documentations, I had instead the idea, that I can do what also works well in such a situation: do the &quot;copycat&quot; 😅.</p>
<p><a href="https://twitter.com" rel="noopener noreferrer external">Twitter</a>, which I am using on a daily basis, is a great example of Progressive Web Apps. Moreover, I am guessing that they have some budget to invest in UX and design development. Therefore, why not using their best practice to unleash our features instead of reinventing the wheels.</p>
<p>Thank you Twitter 🙏</p>
<hr />
<h3>Sneak Peek</h3>
<p>In this post I share the answers and resources we used to add App Shortcuts and maskable icons to our editor for presentations.</p>
<iframe width="280" height="158" src="https://www.youtube.com/embed/na52DkSdrA8" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe
<br/>
<hr />
<h3>App Shortcuts</h3>
<p>App Shortcuts are now supported by <a href="https://developers.google.com/web/updates/2020/07/nic84" rel="noopener noreferrer external">Chrome version 84</a> and <a href="https://geekermag.com/microsoft-edge-pwas-app-icon-shortcut-menu/" rel="noopener noreferrer external">Microsoft Edge</a>.</p>
<p>Moreover, Progressive Web Apps available in Google Play do also support shortcuts. We are using the <a href="https://www.pwabuilder.com/" rel="noopener noreferrer external">PWA builder</a> to convert our PWA to the store’s requested TWA format. They recently upgraded their tool to also automatically supports these links, so once again, thank your PWA builder team for the improvements 👍.</p>
<p>Between all tutorials, the one of <a href="https://web.dev/app-shortcuts/" rel="noopener noreferrer external">web.dev</a> is a great resource to get started. It describes which options can or should be provided to add such shortcuts to a web app.</p>
<p>Summarized, it works with a lit of <code>shortcuts</code> which can be added to the <code>web app manifest</code> . For example, we can create two shortcuts as following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#8BE9FE">  "</span><span style="color:#8BE9FD">name</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Player FM</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">  "</span><span style="color:#8BE9FD">start_url</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">https://player.fm?utm_source=homescreen</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span>  </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">…</span></span>
<span class="line"><span style="color:#8BE9FE">  "</span><span style="color:#8BE9FD">shortcuts</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#F8F8F2">    {</span></span>
<span class="line"><span style="color:#8BE9FE">      "</span><span style="color:#8BE9FD">name</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Open Play Later</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">      "</span><span style="color:#8BE9FD">short_name</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Play Later</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">      "</span><span style="color:#8BE9FD">description</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">View the list of podcasts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">      "</span><span style="color:#8BE9FD">url</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">/play-later?utm_source=homescreen</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">      "</span><span style="color:#8BE9FD">icons</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [{ </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">src</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">/icons/play.png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">sizes</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">192x192</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> }]</span></span>
<span class="line"><span style="color:#F8F8F2">    },</span></span>
<span class="line"><span style="color:#F8F8F2">    {</span></span>
<span class="line"><span style="color:#8BE9FE">      "</span><span style="color:#8BE9FD">name</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">View Subscriptions</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">      "</span><span style="color:#8BE9FD">short_name</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Subscriptions</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">      "</span><span style="color:#8BE9FD">description</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">View the list of podcasts you listen to</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">      "</span><span style="color:#8BE9FD">url</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">/subscriptions?utm_source=homescreen</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">      "</span><span style="color:#8BE9FD">icons</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [{ </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">src</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">/icons/subs.png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">sizes</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">192x192</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> }]</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">  ]</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Moreover than the technical questions, I was asking my self which colors are the most suited for these icons? Our logo being blue, should I also provide blue icons? Or should I use contrasting colors? Or other colors taken from our palette and identity?</p>
<p>That’s where Twitter came to the rescue for first time 😉.</p>
<p>I began by having a look at the HTML source of Twitter to find out the url of their <code>web app manifest</code> .</p>
<p><img src="https://daviddalbusco.com/assets/images/1*nTKC1LdR6zH2-g5OGCNhtg.png" alt="" / loading="lazy"></p>
<p>Once found, I opened the <a href="https://twitter.com/manifest.json" rel="noopener noreferrer external">manifest</a> file and had a look at their <code>shortcuts</code> section. They are providing four shortcuts (“New Tweet”, “Explore”, “Notifications” and “Direct Messages”).</p>
<p><img src="https://daviddalbusco.com/assets/images/1*gcRMTV-lQhSH8v8-ZVUo0g.png" alt="" / loading="lazy"></p>
<p>Finally, I opened these icons to answer my question: yes, they don’t use their primary color as background for the shortcut’s icons but rather use a contrasting color and the primary as color of the symbol. I also noticed that the contrasting color was not white but rather a light grey (#F5F5F5).</p>
<p>Furthermore, it also answered another question I was asking my self: no, shortcuts icons are not maskable, these are regular icons and therefore, can be round.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*rd2urydS262GndrprB7cwQ.png" alt="" / loading="lazy"></p>
<p>As I was about to finalize my icons with these colors, a new question bumped into my mind: what about the safe area?</p>
<p>To solve this new question, I downloaded the Twitter shortcut icon, imported it in my design tool (<a href="https://affinity.serif.com/fr/designer/" rel="noopener noreferrer external">Affinity Designer</a>) and resized mine as it matched the same size. Told you, why reinventing the wheels 🤷.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*jtC-IqCQFXv56ziJJkjHAg.png" alt="" / loading="lazy"></p>
<p>That was it. My icons were ready to be added as shortcuts.</p>
<hr />
<h4>Summary</h4>
<p>Not a summary of what are App Shortcuts but a summary of what Twitter does regarding my related questions:</p>
<ol>
<li>Use a contrasting color for the background of the shortcuts icons and use your<br />
primary color for the symbol.</li>
<li>For these background, a light grey color (#F5F5F5) can be used.</li>
<li>The shortcuts’ icons are regular icons. 192x192 PNG. They can be round.</li>
</ol>
<hr />
<h3>Maskable Icons</h3>
<p>Maskable is an icon format to use adaptive icons on supporting platforms. In other terms: provide an icon to your Progressive Web Apps which can be either displayed in a circle, rounded rectangle, square, drop or cylinder according the device expectations.</p>
<p>The web.dev team provided again a handy <a href="https://web.dev/maskable-icon/" rel="noopener noreferrer external">tutorial</a> about the subject and even linked some nice tools, such as <a href="https://maskable.app/" rel="noopener noreferrer external">Maskable.app</a>, which help create such icons.</p>
<p>While I was developing these for our editor, even though everything seemed clear at first, I was not sure at some point if only maskable icons had to be provided or if I had to provide regular icons too? If both can be provided, how exactly the <code>purpose</code> field of the <code>web app manifest</code> had to be specified?</p>
<p>I notably was unsure about the best way to approach this because when I ran the first test, I noticed that Chrome was displaying in its tab bar our super cool logo as a square which to my eyes, wasn’t that cute anymore in comparison to a circle or as previously, our shiny bubble.</p>
<p>Once again, Twitter come to the rescue. I checked their <a href="https://twitter.com/manifest.json" rel="noopener noreferrer external">manifest</a> and noticed that indeed they are providing four icons.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*--XhgCYeqy5-tBR4lL70nQ.png" alt="" / loading="lazy"></p>
<p>I noticed that they are providing two pairs of icons, the regular and maskable one, both with two sizes, 192x192 and 512x512, and more important, that they are providing the field <code>purpose</code> only for the maskable one.</p>
<p>I set our definition the same way. I was happy to notice that maskable icons were still use on my Android phone and that our sweet logo was back in the form I wanted in the Chrome tab bar 🥳.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*yU-hbKFs8p--b9O64PYZ7A.png" alt="" / loading="lazy"></p>
<hr />
<h4>Summary</h4>
<p>Not a summary of how to apply and create maskable icons but a summary of what Twitter does regarding my related questions:</p>
<ol>
<li>Yes, both regular and maskable icons can be provided.</li>
<li>If so, the field <code>purpose</code> of the <code>icons</code> in the <code>web app manifest</code> should be only specified for the maskable ones, respectively the regular ones don’t have to have a <code>purpose</code> .</li>
</ol>
<hr />
<h3>Altogether</h3>
<p>Because we are open source, let me point out the fact that you can find these related <a href="https://github.com/deckgo/deckdeckgo/tree/master/studio/src/assets/favicon" rel="noopener noreferrer external">icons</a> in our repo and our following <a href="https://github.com/deckgo/deckdeckgo/blob/master/studio/src/manifest.json" rel="noopener noreferrer external">web app manifest</a> as well.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">name</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">DeckDeckGo</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">short_name</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">DeckDeckGo</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">display</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">standalone</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">theme_color</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">#ffffff</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">background_color</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">#3a81fe</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">scope</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">/</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">start_url</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">/</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">orientation</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">portrait</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">icons</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#F8F8F2">        {</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">src</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">/assets/favicon/icon-default-192x192.png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">sizes</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">192x192</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">type</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">image/png</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">        },</span></span>
<span class="line"><span style="color:#F8F8F2">        {</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">src</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">/assets/favicon/icon-default-512x512.png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">sizes</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">512x512</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">type</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">image/png</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">        },</span></span>
<span class="line"><span style="color:#F8F8F2">        {</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">purpose</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">maskable</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">src</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">/assets/favicon/android-chrome-192x192.png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">sizes</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">192x192</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">type</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">image/png</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">        },</span></span>
<span class="line"><span style="color:#F8F8F2">        {</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">purpose</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">maskable</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">src</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">/assets/favicon/android-chrome-512x512.png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">sizes</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">512x512</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">type</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">image/png</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    ],</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">shortcuts</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#F8F8F2">        {</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">name</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Write a presentation</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">short_name</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Write a presentation</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">description</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Start writing a new presentation</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">url</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">/editor</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">icons</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [{ </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">src</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">/assets/favicon/shortcut-editor-192x192.png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">sizes</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">192x192</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> }]</span></span>
<span class="line"><span style="color:#F8F8F2">        },</span></span>
<span class="line"><span style="color:#F8F8F2">        {</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">name</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Dashboard</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">short_name</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Dashboard</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">description</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Access your dashboard</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">url</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">/dashboard</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">            "</span><span style="color:#8BE9FD">icons</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [{ </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">src</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">/assets/favicon/shortcut-dashboard-192x192.png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">sizes</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">192x192</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> }]</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    ]</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>Summary</h3>
<p>Beside hoping that this blog post will be useful to someone, someday, I hope that it also transmit the idea that approaching design and UX questions is also having a look at what other do.</p>
<p>I was asked a couple of times by other developers how I design apps, even though I am not a designer, don’t feel like one and probably never gonna be one.</p>
<p>My answer is always them same: I kind of see my self as a “copycat”. I find things I like, I try my best to develop by myself something “a-ok” inspired from these.</p>
<p>Having a dope designer in a team is definitely an asset but if unfortunately you cannot have one, such approach might help I hope. Inspiration is somehow an essential part of the creation no?</p>
<p>To infinity and beyond!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Bundle A CSS Library]]></title>
          <description><![CDATA[How to build your own custom CSS library with SASS, postcss and clean-css.]]></description>
          <link>https://daviddalbusco.com/blog/bundle-a-css-library/</link>
          <pubDate>Wed, 01 Jul 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*ktUiN4HuCz_nkRrL96xfFg.jpeg" alt="" / loading="lazy"><br />
<em>Photo by <a href="https://unsplash.com/@kobuagency?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">KOBU Agency</a> on <a href="https://medium.com/s/photos/css?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>We have build <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a> in a relatively fragmented way 😅. There is our web editor, which can be used to create and showcase slides and which can also be automatically synchronized with our remote control. There is also a developer kit, which supports HTML or markdown and there is even another version of it, we only used to deploy online your presentations as Progressive Web Apps.</p>
<p>All these multiple applications and kits have in common the fact that they share the exact same core and features, regardless of their technologies, thanks to Web Components made with <a href="https://stenciljs.com/" rel="noopener noreferrer external">Stencil</a>.</p>
<p>These do also have to share the same layout capabilities. For example, if we define a 32px root font size in full screen mode, it should be applied anywhere and therefore, should be spread easily and consistently across our eco system.</p>
<p>That’s why we had to create our own custom CSS library and why I am sharing with you, how you can also bundle such a utility for your own projects.</p>
<hr />
<h3>Credits</h3>
<p>This solution is the one implemented by the CSS framework <a href="https://bulma.io/" rel="noopener noreferrer external">Bulma</a>. No need to reinvent the wheel when it is already wonderfully solved. Thank you Bulma for being <a href="https://github.com/jgthms/bulma" rel="noopener noreferrer external">open source</a> 🙏.</p>
<hr />
<h3>Getting Started</h3>
<p>To initialize our library, we create a new folder, for example <code>bundle-css</code>, and are describing it with a new <code>package.json</code> file. It should contain the name of the library, its version, which is the main file, in our case an (upcoming) <code>sass</code> entry file, the author and a license. Of course, you can add more details, but these give us a quick basis.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">name</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">bundle-css</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">version</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">1.0.0</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">main</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">index.scss</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">author</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">David</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">license</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">MIT</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>In a new folder <code>src</code> we create our style sheet file <code>index.scss</code> . As I was mentioning the <code>fullscreen</code> mode in my introduction, we can for example add a full screen specific style to our file to apply a blue background to the children paragraphs of a “main” element.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B;font-style:italic">:fullscreen</span><span style="color:#50FA7B;font-style:italic"> #main</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">{</span></span>
<span class="line"><span style="color:#FF79C6">  p</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">{</span></span>
<span class="line"><span style="color:#8BE9FD">    background</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">#3880ff</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">;</span></span>
<span class="line"><span>  </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">}</span></span>
<span class="line"><span style="color:#FF5555;font-style:italic;text-decoration:underline">}</span></span></code></pre><hr />
<h3>Clean output</h3>
<p>We might probably want to ensure that every time we build our lib, the outcome does not contain any previous style we would have deleted previously.</p>
<p>That’s why we firstly add <a href="https://github.com/isaacs/rimraf" rel="noopener noreferrer external">rimraf</a> to our project to delete the output folder at begin of each build.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> i</span><span style="color:#F1FA8C"> rimraf</span><span style="color:#BD93F9"> -D</span></span></code></pre><p><em>Note that all dependencies we are adding to our project have to be added as development dependencies because none of these are part of the output.</em></p>
<p>Once rimraf installed, we can initiate our <code>build</code> by editing the <code>scripts</code> in <code>package.json</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#E9F284">"</span><span style="color:#F1FA8C">scripts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">: {</span></span>
<span class="line"><span style="color:#8BE9FE">  "</span><span style="color:#8BE9FD">build</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">rimraf css</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>I selected <code>css</code> for the name of the output folder which will contains the CSS output. You can use another name, what does matter, is adding it to the file <code>package.json</code> in order to include it in the final bundle you might later install in your app or publish to <a href="https://npmjs.com/" rel="noopener noreferrer external">npm</a>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#E9F284">"</span><span style="color:#F1FA8C">files</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">: [</span></span>
<span class="line"><span style="color:#E9F284">  "</span><span style="color:#F1FA8C">css</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">]</span></span></code></pre><p>At this point, altogether, our <code>package.json</code> should contains the following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">name</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">bundle-css</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">version</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">1.0.0</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">main</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">index.scss</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">scripts</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">build</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">rimraf css</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    },</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">files</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">css</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">author</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">David</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">license</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">MIT</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">devDependencies</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">rimraf</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">^3.0.2</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>SASS</h3>
<p>We are using the <a href="https://sass-lang.com/" rel="noopener noreferrer external">SASS</a> extension to edit the styles. Therefore, we have to compile it to CSS. For such purpose, we are using the <a href="https://github.com/sass/node-sass" rel="noopener noreferrer external">node-sass</a> compiler.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> i</span><span style="color:#F1FA8C"> node-sass</span><span style="color:#BD93F9"> -D</span></span></code></pre><p>We enhance our <code>package.json</code> with a new script, which should take care of compiling to CSS, and we are chaining it with our main <code>build</code> script.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#E9F284">"</span><span style="color:#F1FA8C">scripts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">: {</span></span>
<span class="line"><span style="color:#8BE9FE">  "</span><span style="color:#8BE9FD">build</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">rimraf css &#x26;&#x26; npm run build-sass</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">  "</span><span style="color:#8BE9FD">build-sass</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">node-sass --output-style expanded src/index.scss ./css/index.css</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>We provide the input file and specify the output as compilation parameters. We are also using the option <code>expanded</code> to determine the output format of the CSS. It makes it readable and, as we are about to minify it at a later stage of the pipeline, we do not have yet to spare the size of the newlines.</p>
<p>If we give a try to our build script by running <code>npm run build</code> , we should discover an output file <code>/css/index.css</code> which has been converted from <code>SASS</code> to <code>CSS</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6;font-style:italic">:</span><span style="color:#50FA7B;font-style:italic">fullscreen</span><span style="color:#50FA7B;font-style:italic"> #main</span><span style="color:#FF79C6"> p</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD">    background</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> #3880ff</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>Autoprefixing</h3>
<p>In order to support older browser and Safari, it is worth to <a href="https://caniuse.com/#search=fullscreen" rel="noopener noreferrer external">prefix</a> the selector <code>:fullscreen</code> . This can also be the case for other selectors. To parse automatically CSS and add vendor prefixes to CSS rules, using values from <a href="https://caniuse.com/" rel="noopener noreferrer external">Can I Use</a>, we are using <a href="https://github.com/postcss/autoprefixer" rel="noopener noreferrer external">autoprefixer</a>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> i</span><span style="color:#F1FA8C"> autoprefixer</span><span style="color:#F1FA8C"> postcss-cli</span><span style="color:#BD93F9"> -D</span></span></code></pre><p>We are now, again, adding a new build script to our <code>package.json</code> and are chaining this step after the two we have already declared because our goal is to prefix the values once the CSS has been created.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#E9F284">"</span><span style="color:#F1FA8C">scripts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">: {</span></span>
<span class="line"><span style="color:#8BE9FE">  "</span><span style="color:#8BE9FD">build</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">rimraf css &#x26;&#x26; npm run build-sass &#x26;&#x26; npm run build-autoprefix</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span>   </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">...</span></span>
<span class="line"><span style="color:#8BE9FE">  "</span><span style="color:#8BE9FD">build-autoprefix</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">postcss --use autoprefixer --map --output ./css/index.css ./css/index.css</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>With this new command, we are overwriting the CSS file with the new values, and are generating a <code>map</code> file which can be useful for debugging purpose.</p>
<p>If we run our build pipeline <code>npm run build</code> , the output <code>css</code> folder should now contain a <code>map</code> file and our <code>index.css</code> output which should have been prefixed as following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6;font-style:italic">:</span><span style="color:#50FA7B;font-style:italic">-webkit-full-screen</span><span style="color:#50FA7B;font-style:italic"> #main</span><span style="color:#FF79C6"> p</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD">    background</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> #3880ff</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"><span style="color:#FF79C6;font-style:italic">:</span><span style="color:#50FA7B;font-style:italic">-ms-fullscreen</span><span style="color:#50FA7B;font-style:italic"> #main</span><span style="color:#FF79C6"> p</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD">    background</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> #3880ff</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"><span style="color:#FF79C6;font-style:italic">:</span><span style="color:#50FA7B;font-style:italic">fullscreen</span><span style="color:#50FA7B;font-style:italic"> #main</span><span style="color:#FF79C6"> p</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD">    background</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> #3880ff</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"><span style="color:#6272A4">/*# sourceMappingURL=index.css.map */</span></span></code></pre><hr />
<h3>Optimization</h3>
<p>Less is always better, that’s why we are in addition optimizing our CSS library with the help of <a href="https://github.com/jakubpawlowicz/clean-css" rel="noopener noreferrer external">clean-css</a>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> i</span><span style="color:#F1FA8C"> clean-css-cli</span><span style="color:#BD93F9"> -D</span></span></code></pre><p>By adding a last script to our chain, we are able to create a minified version of our CSS file.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#E9F284">"</span><span style="color:#F1FA8C">scripts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">: {</span></span>
<span class="line"><span style="color:#8BE9FE">  "</span><span style="color:#8BE9FD">build</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">rimraf css &#x26;&#x26; npm run build-sass &#x26;&#x26; npm run build-autoprefix &#x26;&#x26; npm run build-cleancss</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span>  </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">...</span></span>
<span class="line"><span style="color:#8BE9FE">  "</span><span style="color:#8BE9FD">build-cleancss</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">cleancss -o ./css/index.min.css ./css/index.css</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>If we run one last time <code>npm run build</code> we should now discover the minified version of our input CSS in the output folder <code>css</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6;font-style:italic">:</span><span style="color:#50FA7B;font-style:italic">-webkit-full-screen</span><span style="color:#50FA7B;font-style:italic"> #main</span><span style="color:#FF79C6"> p</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD">    background</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> #3880ff</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"><span style="color:#FF79C6;font-style:italic">:</span><span style="color:#50FA7B;font-style:italic">-ms-fullscreen</span><span style="color:#50FA7B;font-style:italic"> #main</span><span style="color:#FF79C6"> p</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD">    background</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> #3880ff</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"><span style="color:#FF79C6;font-style:italic">:</span><span style="color:#50FA7B;font-style:italic">fullscreen</span><span style="color:#50FA7B;font-style:italic"> #main</span><span style="color:#FF79C6"> p</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD">    background</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> #3880ff</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>Altogether</h3>
<p>Summarized, here’s the <code>package.json</code> which contains all dependencies and build steps to create our own custom CSS library.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">name</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">bundle-css</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">version</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">1.0.0</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">main</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">index.scss</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">scripts</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">build</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">rimraf css &#x26;&#x26; npm run build-sass &#x26;&#x26; npm run build-autoprefix &#x26;&#x26; npm run build-cleancss</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">build-sass</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">node-sass --output-style expanded src/index.scss ./css/index.css</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">build-autoprefix</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">postcss --use autoprefixer --map --output ./css/index.css ./css/index.css</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">build-cleancss</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">cleancss -o ./css/index.min.css ./css/index.css</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    },</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">files</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">css</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">author</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">David</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">license</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">MIT</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">devDependencies</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">autoprefixer</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">^9.8.4</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">clean-css-cli</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">^4.3.0</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">node-sass</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">^4.14.1</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">postcss-cli</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">^7.1.1</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">rimraf</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">^3.0.2</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>Summary</h3>
<p>Thanks to many open source projects it is possible to create quickly and easily a library for our custom CSS, that’s amazing.</p>
<p>Give a try to <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a> for your next talk and if you are up to contribute with some improvements to our common <a href="https://github.com/deckgo/deckdeckgo/tree/master/utils/deck" rel="noopener noreferrer external">deck</a> styles build following above steeps, your help is more than welcomed 😃.</p>
<p>To infinity and beyond!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[The State Of Progressive Web Apps Adoption By Developers]]></title>
          <description><![CDATA[Are PWA preached or adopted by developers? Do they use these despite the lack of support on iOS? Here are some hints I gathered with polls the last few days.]]></description>
          <link>https://daviddalbusco.com/blog/the-state-of-progressive-web-apps-adoption-by-developers/</link>
          <pubDate>Sun, 21 Jun 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*CJ0b6w9jioWxBedVLqS6NQ.jpeg" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@ytcount?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">YTCount</a> on <a href="https://medium.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>Following this week’s “<a href="https://twitter.com/dhh/status/1272968382329942017" rel="noopener noreferrer external">Apple vs Hey</a>” story I was curious to know if Progressive Web Apps, which can be seen as a solution to such issues, are mostly preached by developers or are actually already adopted too 🤔.</p>
<p>To answer my questions, I ran some polls on <a href="https://twitter.com/" rel="noopener noreferrer external">Twitter</a> and learned some interesting facts and figures which I would like to share with you in this new blog post.</p>
<hr />
<h3>Limitation</h3>
<p>Neither am I an expert in statistics nor am I an expert in running and interpreting polls. Moreover, the experiment happened on Twitter, the panel of answers were given by the people I can reach through this social network and therefore it might probably not be enough representative.</p>
<p>That’s why do not take anything you are about to read as granted. Please do see these figures and comments as hints, nothing more, nothing less.</p>
<p>Also please do note that I am a Progressive Web Apps aficionado. I can try to remain impartial but of course in case of equality, I might be more positive than negative 😉.</p>
<hr />
<h3>47% Of The Developers Do Not Use PWA</h3>
<p>I am using almost on a daily basis, the Progressive Web Apps of <a href="https://dev.to" rel="noopener noreferrer external">DEV.to</a> and I am now giving a try to the Twitter one too. But beside these, I do not think that I am using any others on my phone on a weekly basis. That’s why I was firstly interested by the question of adoption.</p>
<p>To my surprise, <strong>47%</strong> of the developers are not using any PWA, installed on their phone home screen, on a weekly basis.</p>
<p>Even though I tend to think that probably the number should be lower in reality, as many developers are probably using PWA with their browser, this number is quite important.</p>
<p>Of course the main reason for such a rejection rate, as the next chapter will display, is the lack of proper support for PWA on iOS.</p>
<p>Nevertheless, it still means that almost one of two developers do not use PWA and this makes me personally a bit “worried” for the future of the technology on mobile devices. How is it ever going to be accepted by a wider audience, if even developers who program the applications do not or cannot use these?</p>
<p>On the other hand and on a positive note, I should probably not see the glass half empty but rather half full, **29% **of the developers, one of three, are using two or more PWA on a weekly basis. This instantly makes me believe again 😁.</p>
<p><a href="https://twitter.com/daviddalbusco/status/1273571583685246977?s=20" rel="noopener noreferrer external">Source</a></p>
<p>It is also interesting to note that some developers are not pinning PWA on their home screen but are using multiple (4+) PWA on their desktops on a weekly basis.</p>
<p>This leads me to the following idea: What if the future of Progressive Web Apps is actually not the on mobile phones (in a first place) but rather on desktops? What if actually desktops are going to make these popular before they become popular on mobile phones afterwards? If these assumption are true, would that then mean that Google was right with its <a href="https://www.google.com/chromebook/" rel="noopener noreferrer external">Chrome Book</a> since the begin? Or does Google push forward PWA because they believe in Chrome Book?</p>
<p>That is probably too much interpretation or assumptions, but I am really looking forward to the future to get to know if this evolves in this way or not.</p>
<hr />
<h3>63% Of Those Who Do Not Use PWA Are On iOS</h3>
<p>As I said above, no surprise here, the partial support on iOS is the main reason why the developers do not use PWA. <strong>63%</strong> of these who do not use PWA have iPhones. It means that <strong>30%</strong> of all developers are not using PWA simply because they have a phone made by <a href="https://www.apple.com/" rel="noopener noreferrer external">Apple</a>.</p>
<p><a href="https://twitter.com/daviddalbusco/status/1273941203243974657" rel="noopener noreferrer external">Source</a></p>
<p>Moreover than the partial support, it also seems, again according comments I received, that developers are giving up using PWA on iOS because Apple are not displaying any automatic “Add to home screen” popup, making them confuse about where to find the option. Apple forbidding any other browser to implement such a feature on their devices does not ease the problem.</p>
<p>I gave it a try and that’s correct, the UX is kind of frustrating but it is possible to install PWA on your iOS home screen. If you are interested, proceed as I displayed in the following tweet.</p>
<p><a href="https://twitter.com/daviddalbusco/status/1273986564960509952" rel="noopener noreferrer external">Source</a></p>
<p>Note that you can add any websites to your home screen but only these which are proper PWA are going to act as stand-alone app. Thank you Julio for your accurate <a href="https://twitter.com/jcesarmobile/status/1274625510165884928" rel="noopener noreferrer external">feedbacks</a> 👍.</p>
<p>Speaking of, I also took the opportunity to gave a try to the “Add to home screen” UX of the <a href="https://www.mozilla.org" rel="noopener noreferrer external">Firefox</a> mobile on Android, as I never tried it before. Believe me or not, I think it is actually the best one. The Chrome one is good but I almost had the feeling that Firefox was taking me by the hand and told me “Here David, come, I gonna show you how you can properly add a PWA to your phone”.</p>
<p>I do not know if any designer at Firefox will ever read these lines, but if you do, congratulations, amazing work!</p>
<p><a href="https://twitter.com/daviddalbusco/status/1274341226041442304" rel="noopener noreferrer external">Source</a></p>
<hr />
<h3>8% Of The Developers Rather Like Apps From Google Play</h3>
<p>If 30% of all developers are not using PWA because of their iPhones, <strong>17%</strong> do not use these too, even though they own <a href="https://www.android.com/" rel="noopener noreferrer external">Android</a> phones which are, at least to my eyes, really “PWA friendly”. That’s why I ran a final poll to figure out why?</p>
<p>It took me some times to think about possible solutions and I fear that my suggestions made the poll a bit too oriented. Maybe I should have better use an open question.</p>
<p>That being said, it seems that most developers, <strong>44%</strong> of these who do not use PWA on Android, <strong>7%</strong> of all of them, do not use Progressive Web Apps but rather get applications for the store, respectively Google Play, because they feel like their UX or design is better.</p>
<p>To be honest with you, I do not know how to interpret this fact. To me, there can be ugly and not performant web applications as much as there can be bad “native” applications (or coming from store) for the same reasons. I think that it is all about concept and execution. Regardless of the technologies, if badly implemented or designed, it will not be stunning at the end.</p>
<p><a href="https://twitter.com/daviddalbusco/status/1274324722109595648" rel="noopener noreferrer external">Source</a></p>
<p>Worth to notice: Following a feedback which mentioned that PWA are maybe most suited for low end devices, I was curious to know if <a href="https://www.kaiostech.com/" rel="noopener noreferrer external">Kaios</a> does support Progressive Web Apps. Guess what? They do not just support PWA, it is also possible to publish these in their <a href="https://twitter.com/daviddalbusco/status/1274559473206595584" rel="noopener noreferrer external">store</a>.</p>
<hr />
<h3>Conclusion</h3>
<p>Maybe the future of Progressive Web Apps is the desktop? Or maybe its future on mobile devices is the stores, as it is possible to publish them in both Kaios store and Google Play? Or maybe one day the EU will be able to make Apple become PWA friendly? Who knows…</p>
<p>But for sure, I learned some interesting hints and I still do believe in Progressive Web Apps for the future.</p>
<p>Moreover, I just added a reminder for June 2021 in my calendar to run such polls again, let’s see next year how the subject evolved.</p>
<p>Meanwhile, I am most looking forward to hear your feedbacks and thought. Ping me with your best comments!</p>
<p>To infinity and beyond</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Fullscreen: Practical Tips And Tricks]]></title>
          <description><![CDATA[Toggle the fullscreen mode with any browser, a Sass mixin for polyfill and hide the mouse on inactivity]]></description>
          <link>https://daviddalbusco.com/blog/fullscreen-practical-tips-and-tricks/</link>
          <pubDate>Wed, 10 Jun 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*qyf7I6z6WCwU_jGOBpacng.jpeg" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@korpa?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Jr Korpa</a> on <a href="https://unsplash.com/s/photos/full-screen-wallpapers?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>There are already a dozen of existing tutorial about the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API" rel="noopener noreferrer external">Web Fullscreen API</a>, but as I was restyling last Saturday the toolbar for the presenting mode of <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>, our editor for presentations, I noticed that I never shared the few useful tricks we have implemented.</p>
<p>These are:</p>
<ul>
<li>How to implement a toggle for the fullscreen mode compatible with any browser</li>
<li>Create a Sass mixin to polyfill the fullscreen CSS pseudo-class</li>
<li>Hide the mouse cursor on inactivity</li>
</ul>
<hr />
<h3>Toggle Fullscreen Mode With Any Browser</h3>
<p>The API exposes two functions to toggle the mode, <code>requestFullscreen()</code> to enter the fullscreen or <code>exitFullscreen()</code> for its contrary.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">function</span><span style="color:#50FA7B"> toggleFullScreen</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">document.fullscreenElement) {</span></span>
<span class="line"><span style="color:#F8F8F2">        document.documentElement.</span><span style="color:#50FA7B">requestFullscreen</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">else</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (document.exitFullscreen) {</span></span>
<span class="line"><span style="color:#F8F8F2">            document.</span><span style="color:#50FA7B">exitFullscreen</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Even if the methods are well supported across browser, you might notice on <a href="https://caniuse.com/#search=requestFullscreen" rel="noopener noreferrer external">Caniuse</a> a small yellow note next to some version number.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*EO0i13KYRL7b9d7LxDbT6w.png" alt="" / loading="lazy"></p>
<p><em>Caniuse | Full screen API | Jun 9th 2020</em></p>
<p>Indeed, currently Safari and older browser’s version, are not compatible with the API without prefixing the functions with their respective, well, prefix. That’s why, if you are looking to implement a cross-browser compatible function, it is worth to add these to your method.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">function</span><span style="color:#50FA7B"> toggleFullScreen</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> doc </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> window.document;</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> docEl </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> doc.documentElement;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> requestFullScreen </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#F8F8F2">        docEl.requestFullscreen </span><span style="color:#FF79C6">||</span></span>
<span class="line"><span style="color:#F8F8F2">        docEl.mozRequestFullScreen </span><span style="color:#FF79C6">||</span></span>
<span class="line"><span style="color:#F8F8F2">        docEl.webkitRequestFullScreen </span><span style="color:#FF79C6">||</span></span>
<span class="line"><span style="color:#F8F8F2">        docEl.msRequestFullscreen;</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> cancelFullScreen </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#F8F8F2">        doc.exitFullscreen </span><span style="color:#FF79C6">||</span></span>
<span class="line"><span style="color:#F8F8F2">        doc.mozCancelFullScreen </span><span style="color:#FF79C6">||</span></span>
<span class="line"><span style="color:#F8F8F2">        doc.webkitExitFullscreen </span><span style="color:#FF79C6">||</span></span>
<span class="line"><span style="color:#F8F8F2">        doc.msExitFullscreen;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#FF79C6">        !</span><span style="color:#F8F8F2">doc.fullscreenElement </span><span style="color:#FF79C6">&#x26;&#x26;</span></span>
<span class="line"><span style="color:#FF79C6">        !</span><span style="color:#F8F8F2">doc.mozFullScreenElement </span><span style="color:#FF79C6">&#x26;&#x26;</span></span>
<span class="line"><span style="color:#FF79C6">        !</span><span style="color:#F8F8F2">doc.webkitFullscreenElement </span><span style="color:#FF79C6">&#x26;&#x26;</span></span>
<span class="line"><span style="color:#FF79C6">        !</span><span style="color:#F8F8F2">doc.msFullscreenElement</span></span>
<span class="line"><span style="color:#F8F8F2">    ) {</span></span>
<span class="line"><span style="color:#F8F8F2">        requestFullScreen.</span><span style="color:#50FA7B">call</span><span style="color:#F8F8F2">(docEl);</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">else</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        cancelFullScreen.</span><span style="color:#50FA7B">call</span><span style="color:#F8F8F2">(doc);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p><em>Note that I found the above code in the <em><a href="https://developers.google.com/web/fundamentals/native-hardware/fullscreen" rel="noopener noreferrer external">Google Web Fundamentals</a></em>.</em></p>
<hr />
<h3>Sass Mixin</h3>
<p>The <code>:fullscreen</code> CSS pseudo-class (documented <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:fullscreen" rel="noopener noreferrer external">here</a>) is useful to style element according the fullscreen mode.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B;font-style:italic">#myId</span><span style="color:#FF79C6;font-style:italic">:</span><span style="color:#50FA7B;font-style:italic">fullscreen</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD">    background</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> red</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">#myId</span><span style="color:#FF79C6;font-style:italic">:</span><span style="color:#50FA7B;font-style:italic">not</span><span style="color:#FF79C6">(</span><span style="color:#FF79C6;font-style:italic">:</span><span style="color:#50FA7B;font-style:italic">fullscreen</span><span style="color:#FF79C6">)</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD">    background</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> blue</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>It is well supported across browser, as displayed by <a href="https://caniuse.com/#search=%3Afullscreen" rel="noopener noreferrer external">Caniuse</a>, but you might also again notice some limitation, specially when it comes to Safari. That’s why it might be interesting to polyfill the pseudo-class.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*s3cBdYOjRVpc_2PhYxmyzw.png" alt="" / loading="lazy"></p>
<p><em>Caniuse | Full screen API | Jun 9th 2020</em></p>
<p>Moreover, if many elements have to be tweaked regarding the mode, it might interesting to use <a href="https://sass-lang.com/" rel="noopener noreferrer external">Sass</a> and a mixin. That’s why, here is the one we are using.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">@mixin</span><span style="color:#50FA7B"> fullscreen()</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">{</span></span>
<span class="line"><span style="color:#8BE9FD">  #{if(&#x26;</span><span style="color:#6272A4">,</span><span style="color:#F1FA8C"> "&#x26;"</span><span style="color:#6272A4">,</span><span style="color:#F1FA8C"> "*"</span><span style="color:#8BE9FD">)}</span><span style="color:#50FA7B;font-style:italic">:-moz-full-screen</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">{</span></span>
<span class="line"><span style="color:#FF79C6">    @content;</span></span>
<span class="line"><span>  </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">}</span></span>
<span class="line"><span style="color:#8BE9FD">  #{if(&#x26;</span><span style="color:#6272A4">,</span><span style="color:#F1FA8C"> "&#x26;"</span><span style="color:#6272A4">,</span><span style="color:#F1FA8C"> "*"</span><span style="color:#8BE9FD">)}</span><span style="color:#50FA7B;font-style:italic">:-webkit-full-screen</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">{</span></span>
<span class="line"><span style="color:#FF79C6">    @content;</span></span>
<span class="line"><span>  </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">}</span></span>
<span class="line"><span style="color:#8BE9FD">  #{if(&#x26;</span><span style="color:#6272A4">,</span><span style="color:#F1FA8C"> "&#x26;"</span><span style="color:#6272A4">,</span><span style="color:#F1FA8C"> "*"</span><span style="color:#8BE9FD">)}</span><span style="color:#50FA7B;font-style:italic">:-ms-fullscreen</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">{</span></span>
<span class="line"><span style="color:#FF79C6">    @content;</span></span>
<span class="line"><span>  </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">}</span></span>
<span class="line"><span style="color:#8BE9FD">  #{if(&#x26;</span><span style="color:#6272A4">,</span><span style="color:#F1FA8C"> "&#x26;"</span><span style="color:#6272A4">,</span><span style="color:#F1FA8C"> "*"</span><span style="color:#8BE9FD">)}</span><span style="color:#F8F8F2">:</span><span style="color:#8BE9FD">fullscreen </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">{</span></span>
<span class="line"><span style="color:#FF79C6">    @content;</span></span>
<span class="line"><span>  </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">}</span></span>
<span class="line"><span style="color:#FF5555;font-style:italic;text-decoration:underline">}</span></span></code></pre><p>With its help, you can now declare it once and group all your fullscreen styles.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">@include</span><span style="color:#8BE9FD"> fullscreen</span><span style="color:#50FA7B">()</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">{</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">  #myId</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">{</span></span>
<span class="line"><span style="color:#8BE9FD">    background</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">blue</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">;</span></span>
<span class="line"><span>  </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">  #myMenu</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">{</span></span>
<span class="line"><span style="color:#8BE9FD">    display</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">none</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">;</span></span>
<span class="line"><span>  </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">  #myFooter</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">{</span></span>
<span class="line"><span style="color:#8BE9FD">    background</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">yellow</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">;</span></span>
<span class="line"><span>  </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">}</span></span>
<span class="line"><span style="color:#FF5555;font-style:italic;text-decoration:underline">}</span></span></code></pre><p><em>I have the filling that I did not write this mixin by myself, entirely at least, but I could not figured out anymore where I did find it, as I am using it since a while now. If you are her/his author, let me know. I would be happy to give you the credits!</em></p>
<hr />
<h3>Hide Mouse Pointer On Inactivity</h3>
<p>Do you also notice, when a presenter has her/his presentation displayed in fullscreen, if the mouse cursor is still displayed somewhere on the screen?</p>
<p>I do notice it and I rather like to have it hidden 😆. And with rather like I mean that when I noticed this behavior in <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>, I had to develop a solution asap. even if I was spending surf holidays in India (you can check my GitHub commit history, I am not joking, true story 🤣).</p>
<p><img src="https://daviddalbusco.com/assets/images/1*krc3a3DHYjqUkw-CQjaLEw.gif" alt="" / loading="lazy"></p>
<p>In order to detect the inactivity, we listen to the event <a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/mousemove_event" rel="noopener noreferrer external">mousemove</a>. Each time the event is fired, we reset a timer and delay the modification of the style <a href="https://developer.mozilla.org/fr/docs/Web/CSS/cursor" rel="noopener noreferrer external">cursor</a> to hide the mouse. Likewise, if we are toggling between fullscreen and normal mode, we proceed with the same function.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;!</span><span style="color:#FF79C6">doctype</span><span style="color:#50FA7B;font-style:italic"> html</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">html</span><span style="color:#50FA7B;font-style:italic"> dir</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ltr</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">en</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">head</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">meta</span><span style="color:#50FA7B;font-style:italic"> charset</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">utf-8</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">meta</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            name</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">viewport</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            content</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=5.0</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">        /></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">title</span><span style="color:#F8F8F2">>Hide mouse on inactivity&#x3C;/</span><span style="color:#FF79C6">title</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">head</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">body</span><span style="color:#50FA7B;font-style:italic"> style</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">margin: 0; overflow: hidden;</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#50FA7B;font-style:italic"> type</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">text/javascript</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#FF79C6">            let</span><span style="color:#F8F8F2"> idleMouseTimer;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">            document.</span><span style="color:#50FA7B">addEventListener</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">mousemove</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, mouseTimer);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">            function</span><span style="color:#50FA7B"> mouseTimer</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#50FA7B">                showHideMouseCursor</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">true</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">                clearTimer</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">                if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#50FA7B">isFullscreen</span><span style="color:#F8F8F2">()) {</span></span>
<span class="line"><span style="color:#FF79C6">                    return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">                }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">                idleMouseTimer </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> setTimeout</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">                    showHideMouseCursor</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">false</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">                }, </span><span style="color:#BD93F9">2000</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">            function</span><span style="color:#50FA7B"> clearTimer</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">                if</span><span style="color:#F8F8F2"> (idleMouseTimer </span><span style="color:#FF79C6">></span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#50FA7B">                    clearTimeout</span><span style="color:#F8F8F2">(idleMouseTimer);</span></span>
<span class="line"><span style="color:#F8F8F2">                }</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">            function</span><span style="color:#50FA7B"> isFullscreen</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">                return</span><span style="color:#F8F8F2"> window.innerHeight </span><span style="color:#FF79C6">==</span><span style="color:#F8F8F2"> screen.height;</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">            function</span><span style="color:#50FA7B"> showHideMouseCursor</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">show</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">                document.</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">div</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">).style.</span><span style="color:#50FA7B">setProperty</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">cursor</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, show </span><span style="color:#FF79C6">?</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">initial</span><span style="color:#E9F284">"</span><span style="color:#FF79C6"> :</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">none</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">            function</span><span style="color:#50FA7B"> toggleFullScreen</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">                const</span><span style="color:#F8F8F2"> doc </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> window.document;</span></span>
<span class="line"><span style="color:#FF79C6">                const</span><span style="color:#F8F8F2"> docEl </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> doc.documentElement;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">                const</span><span style="color:#F8F8F2"> requestFullScreen </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#F8F8F2">                    docEl.requestFullscreen </span><span style="color:#FF79C6">||</span></span>
<span class="line"><span style="color:#F8F8F2">                    docEl.mozRequestFullScreen </span><span style="color:#FF79C6">||</span></span>
<span class="line"><span style="color:#F8F8F2">                    docEl.webkitRequestFullScreen </span><span style="color:#FF79C6">||</span></span>
<span class="line"><span style="color:#F8F8F2">                    docEl.msRequestFullscreen;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">                const</span><span style="color:#F8F8F2"> cancelFullScreen </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#F8F8F2">                    doc.exitFullscreen </span><span style="color:#FF79C6">||</span></span>
<span class="line"><span style="color:#F8F8F2">                    doc.mozCancelFullScreen </span><span style="color:#FF79C6">||</span></span>
<span class="line"><span style="color:#F8F8F2">                    doc.webkitExitFullscreen </span><span style="color:#FF79C6">||</span></span>
<span class="line"><span style="color:#F8F8F2">                    doc.msExitFullscreen;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">                if</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#FF79C6">                    !</span><span style="color:#F8F8F2">doc.fullscreenElement </span><span style="color:#FF79C6">&#x26;&#x26;</span></span>
<span class="line"><span style="color:#FF79C6">                    !</span><span style="color:#F8F8F2">doc.mozFullScreenElement </span><span style="color:#FF79C6">&#x26;&#x26;</span></span>
<span class="line"><span style="color:#FF79C6">                    !</span><span style="color:#F8F8F2">doc.webkitFullscreenElement </span><span style="color:#FF79C6">&#x26;&#x26;</span></span>
<span class="line"><span style="color:#FF79C6">                    !</span><span style="color:#F8F8F2">doc.msFullscreenElement</span></span>
<span class="line"><span style="color:#F8F8F2">                ) {</span></span>
<span class="line"><span style="color:#F8F8F2">                    requestFullScreen.</span><span style="color:#50FA7B">call</span><span style="color:#F8F8F2">(docEl);</span></span>
<span class="line"><span style="color:#F8F8F2">                } </span><span style="color:#FF79C6">else</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">                    cancelFullScreen.</span><span style="color:#50FA7B">call</span><span style="color:#F8F8F2">(doc);</span></span>
<span class="line"><span style="color:#F8F8F2">                }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">                mouseTimer</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> style</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">display: block; width: 100vw; height: 100vh;</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">button</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                onclick</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#50FA7B">toggleFullScreen</span><span style="color:#F8F8F2">()</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                style</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">position: absolute; </span></span>
<span class="line"><span style="color:#F1FA8C">                     top: 50%; left: 50%; </span></span>
<span class="line"><span style="color:#F1FA8C">                     transform: translate(-50%, -50%);</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">            ></span></span>
<span class="line"><span style="color:#F8F8F2">                Toggle fullscreen</span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#FF79C6">button</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">body</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">html</span><span style="color:#F8F8F2">></span></span></code></pre><hr />
<h3>Conclusion</h3>
<p>I hope that these above tips, we did apply in our editor and developer kit, are going to be useful to someone, somewhere, someday. If you have any questions, ping me with any comments.</p>
<p>Give a try to <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a> for your next presentation 😇.</p>
<p>To infinity and beyond</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Dynamically Import CSS]]></title>
          <description><![CDATA[Lazy load CSS with the help of dynamic import() demonstrated in a Stencil Web Components]]></description>
          <link>https://daviddalbusco.com/blog/dynamically-import-css/</link>
          <pubDate>Mon, 01 Jun 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*01MP69dykx8YMQqMeRB2OQ.jpeg" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@dcemr_e?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Joshua Eckstein</a> on <a href="https://unsplash.com/s/photos/dynamic?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>We recently <a href="https://twitter.com/deckdeckgo/status/1266049044721340417" rel="noopener noreferrer external">introduced</a> several theming options to showcase your code in your presentations made with our editor, <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>.</p>
<p>If you sometimes read my posts, you might already be aware I do care about performances and that I tend to use the lazy loading concept as much as I can. That’s why, when <a href="https://twitter.com/BoradAkash" rel="noopener noreferrer external">Akash Board</a> provided a PR to add this nice set of themes, even if it already worked out like a charm, I was eager to try out the possibility to lazy load these new CSS values. Even if I would spare only a couple of bytes, I thought it was a good experiment and goal, which was of course achieved, otherwise I would not share this new blog post 😉.</p>
<hr />
<h3>Introduction</h3>
<p>The goal of the solution is loading CSS on demand. To achieve such objective, we can take advantage of the JavaScript dynamic <code>import()</code> . Instead of handling static build styles, we defer the loading by integrating the styles as JavaScript code.</p>
<p>In brief, we inject CSS through JavaScript on the fly.</p>
<hr />
<h3>Dynamic Import</h3>
<p>Dynamic <code>import()</code> , which allow asynchronous load and execution of script modules, is part of the <a href="https://github.com/tc39/proposal-dynamic-importhttps://github.com/tc39/proposal-dynamic-import" rel="noopener noreferrer external">official TC39</a> proposal and has been standardized with <a href="https://tc39.es/ecma262/2020/" rel="noopener noreferrer external">ECMAScript 2020</a>. Moreover, it is also already supported by transpiler like <a href="https://webpack.js.org/guides/lazy-loading/" rel="noopener noreferrer external">Webpack</a> or <a href="https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-4.html" rel="noopener noreferrer external">Typescript</a>.</p>
<hr />
<h3>Setup</h3>
<p>Before jumping straight to the solution, let’s start a project with <a href="https://stenciljs.com/" rel="noopener noreferrer external">Stencil</a> with the command line <code>npm init stencil</code>.</p>
<p>This component, we are about to develop for demonstration purpose, has for goal to render a text with either a “green” or “red” background. That’s why we can add such a property to <code>./src/components/my-component/my-component.tsx</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Component, Prop, h } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@stencil/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  tag</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">my-component</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  styleUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">my-component.css</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  shadow</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> MyComponent</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  @</span><span style="color:#50FA7B">Prop</span><span style="color:#F8F8F2">() theme</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">green</span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> |</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">red</span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> =</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">green</span><span style="color:#E9F284">'</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> class</span><span style="color:#FF79C6">={</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.theme</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">>Hello, World!&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>As we are applying the property as class name, we should define the related CSS in <code>./src/components/my-component/my-component.css</code>. Note that we are currently only setting up a demo project, we are not yet implementing the solution, that’s why we add style to CSS file.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6;font-style:italic">:</span><span style="color:#50FA7B;font-style:italic">host</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD">    display</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> block</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">.red</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD">    background</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> red</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">.green</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD">    background</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> green</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Finally, in addition to the component, we also add a <code>&lt;select/&gt;</code> field, which should allow us to switch between these colors, to the <code>./src/index.html</code> for test purpose.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;!</span><span style="color:#FF79C6">doctype</span><span style="color:#50FA7B;font-style:italic"> html</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">html</span><span style="color:#50FA7B;font-style:italic"> dir</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ltr</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">en</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">head</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">meta</span><span style="color:#50FA7B;font-style:italic"> charset</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">utf-8</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">meta</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            name</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">viewport</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            content</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=5.0</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">        /></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">title</span><span style="color:#F8F8F2">>Stencil Component Starter&#x3C;/</span><span style="color:#FF79C6">title</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#50FA7B;font-style:italic"> type</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">module</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/build/lazy-css.esm.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#50FA7B;font-style:italic"> nomodule</span><span style="color:#50FA7B;font-style:italic"> src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/build/lazy-css.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">head</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">body</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">my-component</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">my-component</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">select</span><span style="color:#50FA7B;font-style:italic"> id</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">themeToggler</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> onchange</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#50FA7B">updateTheme</span><span style="color:#F8F8F2">()</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">option</span><span style="color:#50FA7B;font-style:italic"> value</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">green</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> selected</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">selected</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>green&#x3C;/</span><span style="color:#FF79C6">option</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">option</span><span style="color:#50FA7B;font-style:italic"> value</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">red</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>red&#x3C;/</span><span style="color:#FF79C6">option</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#FF79C6">select</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#50FA7B;font-style:italic"> type</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">text/javascript</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#FF79C6">            function</span><span style="color:#50FA7B"> updateTheme</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">                const</span><span style="color:#F8F8F2"> toggler </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> document.</span><span style="color:#50FA7B">getElementById</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">themeToggler</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">                const</span><span style="color:#F8F8F2"> elem </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> document.</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">my-component</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">                elem.theme </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> toggler.value;</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">body</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">html</span><span style="color:#F8F8F2">></span></span></code></pre><p>If we now run the local server, <code>npm run start</code>, to test our component with our favorite browser, we should be able to switch between backgrounds.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*8V3kFZUmrYcph6dePl4eig.gif" alt="" / loading="lazy"></p>
<p>More important, if we open our debugger, we should also that both our styles <code>.green</code> and <code>.red</code> are loaded. It means that the client side as fetch these two styles, even if would have not used for example one of these two colors.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*eeqg_F7tPnDQyIu4-sw2pw.png" alt="" / loading="lazy"></p>
<hr />
<h3>Solution</h3>
<p>Let’s have fun 😜.</p>
<hr />
<h4>Style</h4>
<p>First thing first, we remove the style from <code>./src/components/my-component/my-component.css</code>, from the component’s related CSS.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6;font-style:italic">:</span><span style="color:#50FA7B;font-style:italic">host</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD">    display</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> block</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h4>Functional Component</h4>
<p>Because we have removed the static style, we now need a way to apply them on the fly. That’s why we create a functional component which has for goal to inject <code>&lt;style/&gt;</code> node into our shadowed Web Component.</p>
<p>According the <code>theme</code> property, this new component should either apply the “green” or the “red” background.</p>
<p>For simplicity reason, we declare it into our component main script <code>./src/components/my-component/my-component.tsx</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Component, Prop, h, FunctionalComponent, Host, State} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@stencil/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> ThemeStyle</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> FunctionalComponent</span><span style="color:#F8F8F2">&#x3C;{style</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">}> </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#F8F8F2">  ({</span><span style="color:#FFB86C;font-style:italic">style</span><span style="color:#F8F8F2">}) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#FF79C6">style</span><span style="color:#F8F8F2">></span><span style="color:#FF79C6">{</span><span style="color:#F1FA8C">`</span></span>
<span class="line"><span style="color:#F1FA8C">        :host </span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">style</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">;</span></span>
<span class="line"><span style="color:#F1FA8C">      `</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">style</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  tag</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">my-component</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  styleUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">my-component.css</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  shadow</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> MyComponent</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  @</span><span style="color:#50FA7B">Prop</span><span style="color:#F8F8F2">() theme</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">green</span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> |</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">red</span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> =</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">green</span><span style="color:#E9F284">'</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  @</span><span style="color:#50FA7B">State</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#F8F8F2"> style</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">  // TODO: Dynamically import style</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Host</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ThemeStyle</span><span style="color:#50FA7B;font-style:italic"> style</span><span style="color:#FF79C6">={</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.style</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ThemeStyle</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> class</span><span style="color:#FF79C6">={</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.theme</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">>Hello, World!&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">Host</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h4>Dynamic Import</h4>
<p>The component is set to render dynamically our themes, but we do not yet lazy load these. Moreover, our CSS content has been removed. That’s why we create one JavaScript constant for each and every style we want to fetch at runtime. Concretely, in our project, we create a file <code>./src/components/my-component/red.ts</code> for the “red” theme.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> theme</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#F1FA8C"> `{</span></span>
<span class="line"><span style="color:#F1FA8C">  background: red;</span></span>
<span class="line"><span style="color:#F1FA8C">}`</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#F8F8F2"> {theme};</span></span></code></pre><p>And another one <code>./src/components/my-component/green.ts</code> for the “green” style.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> theme</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#F1FA8C"> `{</span></span>
<span class="line"><span style="color:#F1FA8C">  background: green;</span></span>
<span class="line"><span style="color:#F1FA8C">}`</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#F8F8F2"> {theme};</span></span></code></pre><p>These are the definitions which are going to be executed with the help of dynamic <code>import()</code> which we are finally adding to our component <code>./src/components/my-component/my-component.tsx</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">private async </span><span style="color:#50FA7B">importTheme</span><span style="color:#F8F8F2">(): </span><span style="color:#8BE9FD;font-style:italic">Promise</span><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2">{theme}</span><span style="color:#FF79C6">></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">  if</span><span style="color:#F8F8F2"> (this.theme === </span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">red</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#FF79C6"> import</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">./red</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">  } else  {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#FF79C6"> import</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">./green</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Note that unfortunately it isn’t possible currently to use dynamic <code>import()</code> with a variable. The reason behind, as far as I understand, is that bundler like Webpack or <a href="https://rollupjs.org" rel="noopener noreferrer external">Rollup</a>, even if scripts are going to be injected at runtime, have to know which code is use or not in order to optimize our bundles. That’s why for example <code>return import(</code>${this.theme}<code>);</code> would not be compliant.</p>
<hr />
<h4>Loading</h4>
<p>We have declared our themes and have implemented the <code>import()</code> but we still need to apply the results to the rendering which we do by loading the values when the component is going to be mounted and when the theme property would be modified by declaring a <code>@Watch()</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Component, Prop, h, FunctionalComponent, Host, State, Watch} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@stencil/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> ThemeStyle</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> FunctionalComponent</span><span style="color:#F8F8F2">&#x3C;{style</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">}> </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#F8F8F2">  ({</span><span style="color:#FFB86C;font-style:italic">style</span><span style="color:#F8F8F2">}) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#FF79C6">style</span><span style="color:#F8F8F2">></span><span style="color:#FF79C6">{</span><span style="color:#F1FA8C">`</span></span>
<span class="line"><span style="color:#F1FA8C">        :host </span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">style</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">;</span></span>
<span class="line"><span style="color:#F1FA8C">      `</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">style</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  tag</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">my-component</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  styleUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">my-component.css</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  shadow</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> MyComponent</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  @</span><span style="color:#50FA7B">Prop</span><span style="color:#F8F8F2">() theme</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">green</span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> |</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">red</span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> =</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">green</span><span style="color:#E9F284">'</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  @</span><span style="color:#50FA7B">State</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#F8F8F2"> style</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  async</span><span style="color:#50FA7B"> componentWillLoad</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">loadTheme</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  @</span><span style="color:#50FA7B">Watch</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">theme</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#FF79C6"> async</span><span style="color:#50FA7B"> loadTheme</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> {theme} </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">importTheme</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.style </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> theme;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#FF79C6"> async</span><span style="color:#50FA7B"> importTheme</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;{theme}> {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.theme </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">red</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">      return</span><span style="color:#FF79C6"> import</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">./red</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">else</span><span style="color:#F8F8F2">  {</span></span>
<span class="line"><span style="color:#FF79C6">      return</span><span style="color:#FF79C6"> import</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">./green</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Host</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ThemeStyle</span><span style="color:#50FA7B;font-style:italic"> style</span><span style="color:#FF79C6">={</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.style</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ThemeStyle</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> class</span><span style="color:#FF79C6">={</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.theme</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">>Hello, World!&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">Host</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Et voilà, we are able to lazy load CSS using dynamic <code>import()</code> 🎉.</p>
<p>If we test again our component in the browser using the development server (<code>npm run start</code> ), we should notice that it still renders different background according our selection.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*i3lO5coMF-8IugLx1jLvfg.gif" alt="" / loading="lazy"></p>
<p>More important, if we observe the debugger, we should also notice that our theme loads on the fly.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*XaPbuFwYoLKX9-VEAlX8Sg.gif" alt="" / loading="lazy"></p>
<p>Likewise, if we watch out the shadowed elements, we should notice that only the related <code>&lt;style/&gt;</code> node should be contained.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*nq32o3Us1mosIoFXuiZSmw.gif" alt="" / loading="lazy"></p>
<hr />
<h3>Summary</h3>
<p>It was the first time I used dynamic <code>import()</code> to lazy load CSS in a Web Component and I have to admit that I am really happy with the outcome. Moreover, adding these themes for the code displayed in slides made with <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a> is a really nice improvement I think. Give it a try for your next talk 😁.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*kdOK8ra4vmoz8eltHbMRMA.gif" alt="" / loading="lazy"></p>
<p>To infinity and beyond!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[How To Make Your PWA Offline On Demand]]></title>
          <description><![CDATA[Download your Progressive Web Apps content à la Netflix or Spotify]]></description>
          <link>https://daviddalbusco.com/blog/how-to-make-your-pwa-offline-on-demand/</link>
          <pubDate>Thu, 21 May 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*LBBws2VRETowxwxMNKN--w.jpeg" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@kymellis?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Kym Ellis</a> on <a href="https://unsplash.com/s/photos/wifi?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>Finally!</p>
<p>After the introduction of our web open source editor for presentations <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a> last year, one of the most requested feature was being able to work offline.</p>
<p>We have now implemented and launched this new capability and that’s why I would like to share with you our learning: how did we develop such a “download content à la Netflix or Spotify” feature for our Progressive Web Apps.</p>
<iframe width="280" height="158" src="https://www.youtube.com/embed/J_VyyIlLuWg" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe
<br/>
<hr />
<h3>User Experience (UX)</h3>
<p>There are many ways to approach the “offline” subject. One approach I can think of is making the all application, including its content, available offline, all the time.</p>
<p>Another one is what I call a “on demand offline content solution à la <a href="https://www.spotify.com/" rel="noopener noreferrer external">Spotify</a> or <a href="https://www.netflix.com/" rel="noopener noreferrer external">Netflix</a>” solution. An approach you are probably familiar with, as it is the one offered by these platforms which give their users the ability to download locally content, music or movies, only upon requests.</p>
<p>This approach is the one we implemented, and the one I am sharing with you.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*fUjlTckDh0X3VthqvH0ANg.jpeg" alt="" / loading="lazy"></p>
<hr />
<h3>Introduction</h3>
<p>To make the content of our PWA available offline we proceeded with following steps:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">async </span><span style="color:#50FA7B">goOffline</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">  await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">lazyLoad</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">  await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">saveContent</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">  await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">cacheAssets</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">  await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">toggleOffline</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>Lazy Load</h3>
<p>Our presentations are lazy loaded to improve performances. When you are browsing slides, only the current, previous and next one are loaded. Therefore, the first action required in order to go offline is downloading locally all their assets (images, charts data, code languages etc.).</p>
<p>This can also be the case in your app. Imagine you have got a lazy loaded image down at the bottom of a page or in another location not accessed yet by your user. One solution would be to add it to your service worker precaching strategy but if it is dynamic and unknown at build time, you can’t do so.</p>
<p>Fortunately for us, lazy loading is the core of our solution, and it is supported per default by all our Web Components, that’s why in order to start such a process we only had to call one single function.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">private </span><span style="color:#50FA7B">lazyLoad</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">reject</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">      const</span><span style="color:#F8F8F2"> deck </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> document.</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">deckgo-deck</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">      if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">deck) {</span></span>
<span class="line"><span style="color:#50FA7B">        reject</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Deck not found</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">      }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">      await</span><span style="color:#F8F8F2"> deck.</span><span style="color:#50FA7B">lazyLoadAllContent</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">      resolve</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#50FA7B">      reject</span><span style="color:#F8F8F2">(err);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Such process will take care of iterating through all slides and components to load their content. But these are not yet cached automatically unless you would use, as we do, a service worker.</p>
<p>We are relying on <a href="https://developers.google.com/web/tools/workbox/" rel="noopener noreferrer external">Workbox</a> to manage our strategies and are for example caching images as following. Note that we have two distinct strategies in place in order to to avoid CORS and opaque requests issues with third party providers.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">workbox.routing.</span><span style="color:#50FA7B">registerRoute</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#FF5555">    /</span><span style="color:#FF79C6">^</span><span style="color:#FFB86C">(</span><span style="color:#FF5555">?!</span><span style="color:#BD93F9">.</span><span style="color:#FF79C6">*</span><span style="color:#FFB86C">(?:</span><span style="color:#F1FA8C">unsplash</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">giphy</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">tenor</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">firebasestorage</span><span style="color:#FFB86C">))(</span><span style="color:#50FA7B">?=</span><span style="color:#BD93F9">.</span><span style="color:#FF79C6">*</span><span style="color:#FFB86C">(?:</span><span style="color:#F1FA8C">png</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">jpg</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">jpeg</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">svg</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">webp</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">gif</span><span style="color:#FFB86C">))</span><span style="color:#BD93F9">.</span><span style="color:#FF79C6">*</span><span style="color:#FF5555">/</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#FF79C6;font-weight:bold">    new</span><span style="color:#F8F8F2"> workbox.strategies.</span><span style="color:#50FA7B">CacheFirst</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        cacheName</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">images</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        plugins</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#FF79C6;font-weight:bold">            new</span><span style="color:#F8F8F2"> workbox.expiration.</span><span style="color:#50FA7B">Plugin</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">                maxAgeSeconds</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 30</span><span style="color:#FF79C6"> *</span><span style="color:#BD93F9"> 24</span><span style="color:#FF79C6"> *</span><span style="color:#BD93F9"> 60</span><span style="color:#FF79C6"> *</span><span style="color:#BD93F9"> 60</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">                maxEntries</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 60</span></span>
<span class="line"><span style="color:#F8F8F2">            })</span></span>
<span class="line"><span style="color:#F8F8F2">        ]</span></span>
<span class="line"><span style="color:#F8F8F2">    })</span></span>
<span class="line"><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">workbox.routing.</span><span style="color:#50FA7B">registerRoute</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#FF5555">    /</span><span style="color:#FF79C6">^</span><span style="color:#FFB86C">(</span><span style="color:#50FA7B">?=</span><span style="color:#BD93F9">.</span><span style="color:#FF79C6">*</span><span style="color:#FFB86C">(?:</span><span style="color:#F1FA8C">unsplash</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">giphy</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">tenor</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">firebasestorage</span><span style="color:#FFB86C">))(</span><span style="color:#50FA7B">?=</span><span style="color:#BD93F9">.</span><span style="color:#FF79C6">*</span><span style="color:#FFB86C">(?:</span><span style="color:#F1FA8C">png</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">jpg</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">jpeg</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">svg</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">webp</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">gif</span><span style="color:#FFB86C">))</span><span style="color:#BD93F9">.</span><span style="color:#FF79C6">*</span><span style="color:#FF5555">/</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#FF79C6;font-weight:bold">    new</span><span style="color:#F8F8F2"> workbox.strategies.</span><span style="color:#50FA7B">StaleWhileRevalidate</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        cacheName</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">cors-images</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        plugins</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#FF79C6;font-weight:bold">            new</span><span style="color:#F8F8F2"> workbox.expiration.</span><span style="color:#50FA7B">Plugin</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">                maxAgeSeconds</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 30</span><span style="color:#FF79C6"> *</span><span style="color:#BD93F9"> 24</span><span style="color:#FF79C6"> *</span><span style="color:#BD93F9"> 60</span><span style="color:#FF79C6"> *</span><span style="color:#BD93F9"> 60</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">                maxEntries</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 60</span></span>
<span class="line"><span style="color:#F8F8F2">            }),</span></span>
<span class="line"><span style="color:#FF79C6;font-weight:bold">            new</span><span style="color:#F8F8F2"> workbox.cacheableResponse.</span><span style="color:#50FA7B">CacheableResponse</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">                statuses</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">200</span><span style="color:#F8F8F2">]</span></span>
<span class="line"><span style="color:#F8F8F2">            })</span></span>
<span class="line"><span style="color:#F8F8F2">        ]</span></span>
<span class="line"><span style="color:#F8F8F2">    })</span></span>
<span class="line"><span style="color:#F8F8F2">);</span></span></code></pre><p>If you are curious about all strategies we developed, checkout out our <a href="https://github.com/deckgo/deckdeckgo/blob/master/studio/src/sw.js" rel="noopener noreferrer external">sw.js</a> script in our open source repo.</p>
<hr />
<h3>Save Content</h3>
<p>As our users won’t have access to internet anymore, they will not be able to reach the database and fetch their content. That is why it has to be save locally.</p>
<p>Even though we are using <a href="https://firebase.google.com/docs/firestore" rel="noopener noreferrer external">Cloud Firestore</a> and libraries are already offering an “offline first” feature or support, we implemented our own custom solution.</p>
<p>That’s why, we have developed our own concept with the help of IndexedDB. For example, in the following piece of code we are fetching a deck from the online database and are saving it locally. Worth to notice that we are using the element unique identifier as storage key and the handy <a href="https://github.com/jakearchibald/idb-keyval" rel="noopener noreferrer external">idb-keyval</a> store.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {set} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">idb-keyval</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">private </span><span style="color:#50FA7B">saveDeck</span><span style="color:#F8F8F2">(deckId: string): </span><span style="color:#8BE9FD;font-style:italic">Promise</span><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2">Deck</span><span style="color:#FF79C6">></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  return new </span><span style="color:#50FA7B">Promise</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">reject</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // 1. Retrieve data from online DB</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">    const</span><span style="color:#F8F8F2"> deck </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.deckOnlineService.</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(deckId);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    if</span><span style="color:#F8F8F2"> (!</span><span style="color:#FFB86C;font-style:italic">deck</span><span style="color:#F8F8F2"> || !deck.data) {</span></span>
<span class="line"><span style="color:#50FA7B">      reject</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Missing deck</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">      return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#6272A4">    // 2. Save data in IndexedDB</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">    await</span><span style="color:#50FA7B"> set</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`/decks/</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">deck.id</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">deck</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    resolve</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">deck</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>At this point you may ask yourself what’s the point? It is nice to have the content locally saved but it does not mean yet that the user will be able to use it once offline right? Moreover, you may fear that it would need a full rewrite of the application to consume these data isn’t it?</p>
<p>Fortunately, our application was already separated in different layers and with the help of a new global state, which tells if the application is <code>offline</code> or <code>online</code> , we were able to extend our singleton services to make these behave differently with the databases according the mode.</p>
<p>Concretely, if online it interacts with Firestore, if offline, it interacts with IndexedDB.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> DeckService</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#FF79C6"> static</span><span style="color:#F8F8F2"> instance</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> DeckService</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#FF79C6"> constructor</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#6272A4">    // Private constructor, singleton</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  static</span><span style="color:#50FA7B"> getInstance</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">DeckService.instance) {</span></span>
<span class="line"><span style="color:#F8F8F2">      DeckService.instance </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> DeckService</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> DeckService.instance;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  async</span><span style="color:#50FA7B"> get</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">deckId</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Deck</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> offline </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> OfflineService.</span><span style="color:#50FA7B">getInstance</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">status</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (offline </span><span style="color:#FF79C6">!==</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">      return</span><span style="color:#F8F8F2"> DeckOfflineService.</span><span style="color:#50FA7B">getInstance</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(deckId);</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">else</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">      return</span><span style="color:#F8F8F2"> DeckOnlineService.</span><span style="color:#50FA7B">getInstance</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(deckId);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>The interaction with the online database remained the same, therefore we only had to move the function to a new service.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(deckId: string): </span><span style="color:#8BE9FD;font-style:italic">Promise</span><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2">Deck</span><span style="color:#FF79C6">></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  return new </span><span style="color:#50FA7B">Promise</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">reject</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">    const</span><span style="color:#F8F8F2"> firestore </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> firebase.</span><span style="color:#50FA7B">firestore</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">    try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">      const</span><span style="color:#F8F8F2"> snapshot </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> firestore</span></span>
<span class="line"><span style="color:#F8F8F2">        .</span><span style="color:#8BE9FD;font-style:italic">collection</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">decks</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">        .</span><span style="color:#8BE9FD;font-style:italic">doc</span><span style="color:#F8F8F2">(</span><span style="color:#8BE9FD;font-style:italic">deckId</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">        .</span><span style="color:#8BE9FD;font-style:italic">get</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">      if</span><span style="color:#F8F8F2"> (!snapshot.exists) {</span></span>
<span class="line"><span style="color:#50FA7B">        reject</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Deck not found</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">      }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">      const</span><span style="color:#F8F8F2"> deck</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> DeckData</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> snapshot.</span><span style="color:#50FA7B">data</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> DeckData</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">      resolve</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        id: </span><span style="color:#FFB86C;font-style:italic">snapshot</span><span style="color:#F8F8F2">.</span><span style="color:#FFB86C;font-style:italic">id</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        data: </span><span style="color:#FFB86C;font-style:italic">deck</span></span>
<span class="line"><span style="color:#F8F8F2">      });</span></span>
<span class="line"><span style="color:#F8F8F2">    } catch (err) {</span></span>
<span class="line"><span style="color:#50FA7B">      reject</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">err</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Once refactored, we had to create its offline counterpart.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(deckId: string): </span><span style="color:#8BE9FD;font-style:italic">Promise</span><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2">Deck</span><span style="color:#FF79C6">></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  return new </span><span style="color:#50FA7B">Promise</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">reject</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">    try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">      const</span><span style="color:#F8F8F2"> deck</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Deck</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> get</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`/decks/</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">deckId</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">      resolve</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">deck</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    } catch (err) {</span></span>
<span class="line"><span style="color:#50FA7B">      reject</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">err</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>As you can notice, we are using the unique identifier as storage key which makes the all system really handy as we are able to fetch data locally almost as we would do if we would do with the online database. Doing so we did not had to modify the other layers of the application, everything was kind of working offline almost out of the box without any further changes.</p>
<hr />
<h3>Cache Assets</h3>
<p>So far we were able to save locally the users’ data with IndexedDB, cache the content with the Service Worker, therefore all the presentation is available offline, but isn’t something else missing?</p>
<p>Yes indeed, something is still not cached: the assets of the applications itself.</p>
<p>Again this can be solved with a pre-cache strategy but if we are not able too, you would have to find an alternative.</p>
<p>Ours was the following. We created a new <a href="https://github.com/deckgo/deckdeckgo/blob/master/studio/src/assets/assets.json" rel="noopener noreferrer external">JSON file</a> in which we listed each and every assets we are using, including icons and fonts.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span>  </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">...</span></span>
<span class="line"><span style="color:#8BE9FE">  "</span><span style="color:#8BE9FD">navigation</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#F8F8F2">     {</span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">src</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">/icons/ionicons/open.svg</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FE">"</span><span style="color:#8BE9FD">ariaLabel</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Open</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">},</span></span>
<span class="line"><span>     </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">...</span></span>
<span class="line"><span style="color:#FF5555;font-style:italic;text-decoration:underline">}</span></span></code></pre><p>Then, when user requests the offline mode, we iterate through each entry and are calling the Service Worker from the app context to trigger the caching.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> cacheUrls</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">cacheName</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">urls</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">[]) {</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> myCache </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> window.caches.</span><span style="color:#50FA7B">open</span><span style="color:#F8F8F2">(cacheName);</span></span>
<span class="line"><span style="color:#FF79C6">  await</span><span style="color:#F8F8F2"> myCache.</span><span style="color:#50FA7B">addAll</span><span style="color:#F8F8F2">(urls);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>If you are eager to know more about this specific feature, I published earlier this year another <a href="https://daviddalbusco.com/blog/how-to-call-the-service-worker-from-the-web-app-context" rel="noopener noreferrer external">blog post</a> about it.</p>
<hr />
<h3>Toggle Offline</h3>
<p>Finally, as everything is cached and the internet access can now safely be turned off, we can save a global state to instruct our application to works in an offline mode.</p>
<hr />
<h3>Go Online</h3>
<p>You know what’s really cool with the above solution? As we did not modify or limit any core features by “only” caching and adding some layers in our architecture, our users are not just able to read their content offline, it also remains <strong>editable</strong> 🔥.</p>
<p>This means that when users are back online, they should be able to transfer their local content to the remote database.</p>
<p>Such process follow the same logic as the one we developed.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">async </span><span style="color:#50FA7B">goOnline</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">  await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">uploadContent</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">toggleOnline</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>All the local content has to be extracted from the IndexedDB and moreover, all local images or other content the user would have added locally has to be transferred to the remote storage.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">private async </span><span style="color:#50FA7B">uploadDeck</span><span style="color:#F8F8F2">(deck: Deck) {</span></span>
<span class="line"><span style="color:#FF79C6">  await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">uploadDeckLocalUserAssetsToStorage</span><span style="color:#F8F8F2">(deck);</span></span>
<span class="line"><span style="color:#FF79C6">  await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">uploadDeckDataToDb</span><span style="color:#F8F8F2">(deck);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Happy to develop this process further if requested, ping me with your questions 👋.</p>
<hr />
<h3>Summary</h3>
<p>I might only had tipped the top of the iceberg with this article, but I hope that I was at least able to share with you the general idea of our learning and solution.</p>
<p>Of course, I would be also super happy, if you would give our editor a try for your next talk 👉 <a href="https://deckdeckgo.com" rel="noopener noreferrer external">deckdeckgo.com</a>.</p>
<p>To infinity and beyond!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Showcase Your PWA In Your Website]]></title>
          <description><![CDATA[Introducing a new Web Component to embed applications in websites with stylish mobile devices iframes]]></description>
          <link>https://daviddalbusco.com/blog/showcase-your-pwa-in-your-website/</link>
          <pubDate>Fri, 08 May 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*dH35UnXlx3cw1RX4l8KIdw.png" alt="" / loading="lazy"></p>
<p>Two weeks ago, <a href="https://twitter.com/dotNetkow" rel="noopener noreferrer external">Matt Netkow</a> introduced <a href="https://ionicframework.com/docs/react" rel="noopener noreferrer external">Ionic React</a> in an online presentation of the Ionic Zürich Meetup.</p>
<p>After a couple of minutes he <a href="https://youtu.be/g1FRjVALfak?t=524" rel="noopener noreferrer external">displayed</a> a features I never ever had thought about before: he showcased in his presentation an interactive embedded apps 🤯.</p>
<p>This literally let me speachless. What if anybody would be able to embed any interactive applications and websites easily in any slides?</p>
<p>I was convinced as soon as his idea hit my brain and that’s why I am happy to share with you this new feature of <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>, our web editor for presentations, and per extension, the new Web Component we are open sourcing!</p>
<p><img src="https://daviddalbusco.com/assets/images/1*FV6kFmGqYnXD2uIpQf27UA.gif" alt="" / loading="lazy"></p>
<hr />
<h3>Credits</h3>
<p>It is not the first time <a href="https://twitter.com/dotNetkow" rel="noopener noreferrer external">Matt</a> inspired me a feature. If the landing page of our project is also a presentation itself, it is also because once he said that he found the idea interesting. Definitely Matt, thank you for the inspiration!</p>
<p>If you are familiar with the <a href="https://ionicframework.com/" rel="noopener noreferrer external">Ionic</a> ecosystem, you may already have noticed that the device’s frame showcased and used in the above example really looks like the one used in their documentation. I can’t argue about that and you are totally right. Instead of reinventing the wheel, we used the <a href="https://github.com/ionic-team/ionic-docs/tree/c5a624ac35d5285b871e7d8513d3849bdea63271/src/components/demo" rel="noopener noreferrer external">style code</a> they published under MIT license. Thank you Ionic for everything you do for the web 🙏.</p>
<hr />
<h3>Installation</h3>
<p>We are providing some guidance in our <a href="https://docs.deckdeckgo.com/components/demo" rel="noopener noreferrer external">documentation</a> and Stencil is also displaying how any components can be installed in with any <a href="https://stenciljs.com/docs/overview" rel="noopener noreferrer external">frameworks</a>.</p>
<hr />
<h3>Install From A CDN</h3>
<p>To fetch the component from a CDN, as for example <a href="https://unpkg.com/" rel="noopener noreferrer external">Unpkg</a>, add the following to the header of your HTML.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#50FA7B;font-style:italic"> type</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">module</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://unpkg.com/@deckdeckgo/demo@latest/dist/deckdeckgo-demo/deckdeckgo-demo.esm.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#50FA7B;font-style:italic"> nomodule</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">""</span><span style="color:#50FA7B;font-style:italic"> src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://unpkg.com/@deckdeckgo/demo@latest/dist/deckdeckgo-demo/deckdeckgo-demo.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span></code></pre><hr />
<h3>Install From NPM</h3>
<p>To install the project from <a href="https://www.npmjs.com/" rel="noopener noreferrer external">npm</a>, run the following command in your terminal:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> install</span><span style="color:#F1FA8C"> @deckdeckgo/demo</span></span></code></pre><p>According to your need, either import it:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@deckdeckgo/demo</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span></code></pre><p>Or use a custom loader:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { </span><span style="color:#FFB86C;font-style:italic">defineCustomElements</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> deckDeckGoElement } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@deckdeckgo/demo/dist/loader</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">deckDeckGoElement</span><span style="color:#F8F8F2">();</span></span></code></pre><hr />
<h3>Showcase Your Applications</h3>
<p>To use the component and showcase your applications, use it as following where the property <code>src</code> is the URI of your Progressive Web Apps or website.</p>
<p>Note that we are also setting the property <code>instant</code> to <code>true</code> to render instantly the content as the default behavior of the component is set to be lazy loaded. In case of <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>, to maximize the load performances, only the current and next slides’ assets, and therefore <code>iframe</code> too, are loaded iteratively.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">deckgo-demo</span><span style="color:#50FA7B;font-style:italic"> src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://deckdeckgo.app</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> instant</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">true</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">> &#x3C;/</span><span style="color:#FF79C6">deckgo-demo</span><span style="color:#F8F8F2">></span></span></code></pre><p>That’s it, you are showcasing your application 🎉.</p>
<hr />
<h3>Sizing</h3>
<p>The component will automatically calculate the size of its content according the host available size.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">private async </span><span style="color:#50FA7B">initSize</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">      const</span><span style="color:#F8F8F2"> style</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> CSSStyleDeclaration</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> window </span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2"> window.</span><span style="color:#50FA7B">getComputedStyle</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.el) </span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">      const</span><span style="color:#F8F8F2"> width</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> style </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#50FA7B"> parseInt</span><span style="color:#F8F8F2">(style.width) </span><span style="color:#FF79C6">></span><span style="color:#BD93F9"> 0</span><span style="color:#FF79C6"> ?</span></span>
<span class="line"><span style="color:#50FA7B">                   parseInt</span><span style="color:#F8F8F2">(style.width) </span><span style="color:#FF79C6">:</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.el.offsetWidth;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">      const</span><span style="color:#F8F8F2"> height</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> style </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#50FA7B"> parseInt</span><span style="color:#F8F8F2">(style.height) </span><span style="color:#FF79C6">></span><span style="color:#BD93F9"> 0</span><span style="color:#FF79C6"> ?</span></span>
<span class="line"><span style="color:#50FA7B">                    parseInt</span><span style="color:#F8F8F2">(style.height) </span><span style="color:#FF79C6">:</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.el.offsetHeight;</span></span>
<span class="line"><span style="color:#FF79C6">      const</span><span style="color:#F8F8F2"> deviceHeight</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (width </span><span style="color:#FF79C6">*</span><span style="color:#BD93F9"> 704</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">/</span><span style="color:#BD93F9"> 304</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">      this</span><span style="color:#F8F8F2">.width </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> deviceHeight </span><span style="color:#FF79C6">></span><span style="color:#F8F8F2"> height </span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2"> (height </span><span style="color:#FF79C6">*</span><span style="color:#BD93F9"> 304</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">/</span><span style="color:#BD93F9"> 704</span><span style="color:#FF79C6"> :</span><span style="color:#F8F8F2"> width;</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">      this</span><span style="color:#F8F8F2">.height </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> deviceHeight </span><span style="color:#FF79C6">></span><span style="color:#F8F8F2"> height </span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2"> height </span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> deviceHeight;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span></code></pre><p>That’s why, you can either encapsulate it in a container and make it responsive or assign it a size using styling.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">deckgo-demo</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://deckdeckgo.app</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    instant</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">true</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    style</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">width: 40vw; height: 90vh;</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">deckgo-demo</span><span style="color:#F8F8F2">></span></span></code></pre><p>Note also that the component will listen to resizing of the browser. Therefore, each time its size will change, it will resize itself automatically.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">window.</span><span style="color:#50FA7B">removeEventListener</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">resize</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#50FA7B">       debounce</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.onResizeContent, </span><span style="color:#BD93F9">500</span><span style="color:#F8F8F2">));</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">private </span><span style="color:#50FA7B">onResizeContent</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">initSize</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">  await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">updateIFrameSizeReload</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Worth to notice too that in order to be sure that the content of your integrated app fits correctly, on each resize of the browser, it will be reloaded too. This is achieved with the following ugly beautiful hack to reload cross-domain <code>iframe</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">iframe.src </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> iframe.src;</span></span></code></pre><hr />
<h3>Summary</h3>
<p>Moreover than in slides, in which I definitely see a use case for such components because I am already looking forward to use it for my personal talks, I think it might be useful too, if for example, you are displaying a showcase of your realization in your website.</p>
<p>I also hope it made you eager to give <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a> a try for your next presentations 😊.</p>
<p>To infinity and beyond!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Develop A Konami Code For Any Apps With Stencil]]></title>
          <description><![CDATA[How to create  a Web Component with Stencil to add a Konami Code feature in any modern web applications]]></description>
          <link>https://daviddalbusco.com/blog/develop-a-konami-code-for-any-apps-with-stencil/</link>
          <pubDate>Sun, 19 Apr 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*xQKl3mFjtshpfAN2gQZ4Bw.png" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@coopery?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Mohamed Nohassi</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I have shared 35 daily ”<a href="https://daviddalbusco.com/blog/how-to-call-the-service-worker-from-the-web-app-context" rel="noopener noreferrer external">One Trick A Day</a>” blog posts in a row until today, the original scheduled date of the end of the COVID-19 quarantine in Switzerland, April 19th 2020.</p>
<p>This milestone has been postponed but even though we have to continue the effort, some small positive signs have emerged. Hopefully better days are ahead.</p>
<hr />
<p>The <a href="https://en.wikipedia.org/wiki/Konami_Code" rel="noopener noreferrer external">Konami Code</a> is a cheat code which appeared in many Konami video games which allow(ed) players to reveal hidden features or unlock achievements while pressing a sequence of buttons on their game controller: ⬆️, ⬆️, ⬇️, ⬇️, ⬅️, ➡️, ⬅️, ➡️, 🅱️, 🅰️.</p>
<p>As it found a place in the popular culture, many websites or applications are nowadays using it to provide animation which are going to make us, geeks and nerds, smile 😄.</p>
<p>That’s why I thought that it was a good example to introduce <a href="https://stenciljs.com/" rel="noopener noreferrer external">Stencil</a> but a fun idea to conclude this series of articles.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*4ys6BI2a0KM10GxgnxNmpQ.gif" alt="" / loading="lazy"></p>
<hr />
<h3>Get Started</h3>
<p>To get started we create a new standalone components using the Cli.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> init</span><span style="color:#F1FA8C"> stencil</span></span></code></pre><p>When prompted, select <code>component</code> as type of starter and provide <code>konami-code</code> as project name. Once over, jump into the directory and install the dependencies.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#8BE9FD">cd</span><span style="color:#F1FA8C"> konami-code</span><span style="color:#F8F8F2"> &#x26;&#x26; </span><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> install</span></span></code></pre><hr />
<h3>Blank Component</h3>
<p>The starter component is created with some “hello world” type code. That’s why, to make this tutorial easier to follow, we firstly “clean it” a bit.</p>
<p>Note that we are not going to rename the packages and files as we would do if we would publish it to <a href="https://www.npmjs.com/" rel="noopener noreferrer external">npm</a> afterwards.</p>
<p>We edit <code>./src/component/my-component/my-component.tsx</code> to modify the attribute tag in order to use our component as <code>&lt;konami-code/&gt;</code> . Moreover it will also render “Hadouken!” because “Street Fighter II Turbo” put the regular code in before the initial splash screen to enable turbo up to 8 Stars ⭐.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Component, h } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@stencil/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    tag</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">konami-code</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    styleUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">my-component.css</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    shadow</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> MyComponent</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">>Hadouken!&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>We don’t modify yet the CSS but we do modify the <code>./src/index.html</code> for test purpose and to reflect the new <code>tag</code> name.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;!</span><span style="color:#FF79C6">doctype</span><span style="color:#50FA7B;font-style:italic"> html</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">html</span><span style="color:#50FA7B;font-style:italic"> dir</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ltr</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">en</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">head</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">meta</span><span style="color:#50FA7B;font-style:italic"> charset</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">utf-8</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">meta</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            name</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">viewport</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            content</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=5.0</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">        /></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">title</span><span style="color:#F8F8F2">>Stencil Component Starter&#x3C;/</span><span style="color:#FF79C6">title</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#50FA7B;font-style:italic"> type</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">module</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/build/konami-code.esm.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#50FA7B;font-style:italic"> nomodule</span><span style="color:#50FA7B;font-style:italic"> src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/build/konami-code.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">head</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">body</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">>Konami Code&#x3C;/</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">>Develop A "Konami Code" For Any Apps With Stencil&#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">>Hit: ⬆️, ⬆️, ⬇️, ⬇️, ⬅️, ➡️️, ⬅️, ➡️, 🅱️, 🅰️&#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">konami-code</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">konami-code</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">body</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">html</span><span style="color:#F8F8F2">></span></span></code></pre><p>If we run our project ( <code>npm run start</code> ), your default browser should automatically open itself at the address <code>http://localhost:3333</code> and you should be able to see the following elements rendered:</p>
<p><img src="https://daviddalbusco.com/assets/images/1*eQHNSTCKskWew5o9IQvidg.png" alt="" / loading="lazy"></p>
<hr />
<h3>Detection</h3>
<p>Per default we are going to hide our component content and are looking to display it only if a particular sequence of keyboard keys (⬆️, ⬆️, ⬇️, ⬇️, ⬅️, ➡️, ⬅️, ➡️, 🅱️, 🅰️) are going to be hit.</p>
<p>Therefor we can define it in our <code>./src/components/my-component/my-component.tsx</code> as a readonly array.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">private readonly konamiCode: string[] </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#E9F284">  "</span><span style="color:#F1FA8C">ArrowUp</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">  "</span><span style="color:#F1FA8C">ArrowUp</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">  "</span><span style="color:#F1FA8C">ArrowDown</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">  "</span><span style="color:#F1FA8C">ArrowDown</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">  "</span><span style="color:#F1FA8C">ArrowLeft</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">  "</span><span style="color:#F1FA8C">ArrowRight</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">  "</span><span style="color:#F1FA8C">ArrowLeft</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">  "</span><span style="color:#F1FA8C">ArrowRight</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">  "</span><span style="color:#F1FA8C">KeyB</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">  "</span><span style="color:#F1FA8C">KeyA</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">];</span></span></code></pre><p>To listen to events, we generally register and unregister <a href="https://developer.mozilla.org/en-US/docs/Web/API/EventListener" rel="noopener noreferrer external">EventListener</a>. One of the cool thing of Stencil is that it makes possible to do such things by using <a href="https://stenciljs.com/docs/events" rel="noopener noreferrer external">decorators</a>. Pretty neat to keep the code clean.</p>
<p>As we are interested to “track” keyboard keys, we are listening to the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/keydown_event" rel="noopener noreferrer external">keydown</a> event.</p>
<p>Moreover, to compare the list of user keys with the code sequence, we save the keys in a new array. We also take care of limiting its maximal length to the exact same length as the sequence (with <code>shift</code> we remove the first object in the array respectively the oldest key kept in memory) and are finally comparing these as <code>string</code> ( <code>join</code> parse array using the provided delimiter).</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">private keys: string[] </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> [];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Listen</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">keydown</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, { target</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">document</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> })</span></span>
<span class="line"><span style="color:#50FA7B">onKeydown</span><span style="color:#F8F8F2">($event: KeyboardEvent) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">  this</span><span style="color:#F8F8F2">.keys.</span><span style="color:#50FA7B">push</span><span style="color:#F8F8F2">($event.code);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> (</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.keys.length </span><span style="color:#FF79C6">></span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.konamiCode.length) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.keys.</span><span style="color:#50FA7B">shift</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> match </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.konamiCode.</span><span style="color:#50FA7B">join</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">,</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.keys.</span><span style="color:#50FA7B">join</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">,</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>At this point our layout should not change but if we would add a <code>console.log($event.code, match);</code> at the end of our listener function for demo purpose, we should be able to test our component by observing the debugger.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*wQ5VT43wJ_V_1FU1hCDfag.gif" alt="" / loading="lazy"></p>
<hr />
<h3>Conditional Rendering</h3>
<p>To render conditionally the outcome of our code, we introduce a new <a href="https://stenciljs.com/docs/state" rel="noopener noreferrer external">state</a> variable, which, if modified, will cause the component <code>render</code> function to be called again.</p>
<p>We are using it to render conditionally our message “Hadouken!”.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Component, h, Listen, State} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@stencil/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  tag</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">konami-code</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  styleUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">my-component.css</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  shadow</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> MyComponent</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  @</span><span style="color:#50FA7B">State</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#F8F8F2"> match</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#FF79C6"> =</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#FF79C6"> readonly</span><span style="color:#F8F8F2"> konamiCode</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">[] </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">ArrowUp</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">ArrowUp</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">ArrowDown</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">ArrowDown</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">ArrowLeft</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">ArrowRight</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">ArrowLeft</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">ArrowRight</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">KeyB</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">KeyA</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  ];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#F8F8F2"> keys</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">[] </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> [];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  @</span><span style="color:#50FA7B">Listen</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">keydown</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, { target</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">document</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> })</span></span>
<span class="line"><span style="color:#50FA7B">  onKeydown</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">$event</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> KeyboardEvent</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.keys.</span><span style="color:#50FA7B">push</span><span style="color:#F8F8F2">($event.code);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.keys.length </span><span style="color:#FF79C6">></span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.konamiCode.length) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">      this</span><span style="color:#F8F8F2">.keys.</span><span style="color:#50FA7B">shift</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.match </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">         this</span><span style="color:#F8F8F2">.konamiCode.</span><span style="color:#50FA7B">join</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">,</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.keys.</span><span style="color:#50FA7B">join</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">,</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span><span style="color:#FF79C6">{</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.match </span><span style="color:#FF79C6">?</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Hadouken!</span><span style="color:#E9F284">"</span><span style="color:#FF79C6"> :</span><span style="color:#BD93F9"> undefined</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>If you would test it in your browser, you should now notice that the message as per default disappear but that you are able to make it appearing as soon as you have hit the Konami code sequence 🎉.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*v9T9jbZxtk5CwbdXL54iZQ.gif" alt="" / loading="lazy"></p>
<hr />
<h3>Dynamic Content</h3>
<p>You may be interested to let users specify their own message rather “Hadouken!”. After all, maybe some would rather like to display “Shoryuken!” 😁.</p>
<p>That’s why we can transform our fixed text to a <code>&lt;slot/&gt;</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span><span style="color:#FF79C6">{</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.match </span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FF79C6">slot</span><span style="color:#F8F8F2">>Hadouken!&#x3C;/</span><span style="color:#FF79C6">slot</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> undefined</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#F8F8F2">         &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Something I learned recently, we can provide a default value to the <code>&lt;slot/&gt;</code>. Doing so, if a slotted element is provided, it will be displayed, if not, the default “Hadouken!” is going to be used.</p>
<p>For example, <code>&lt;konami-code&gt;&lt;/konami-code&gt;</code> displays “Hadouken!” but <code>&lt;konami-code&gt;Shoryuken!&lt;/konami-code&gt;</code> renders, well, “Shoryuken!”.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*6vLBzlgN4xZ-6xXvbdUfaw.gif" alt="" / loading="lazy"></p>
<hr />
<h3>Style</h3>
<p>Even though it does the job, our component can be a bit styled. That’s why instead of a logical render I suggest that we modify it to be <code>visible</code> or not.</p>
<p>We can also maybe display the message in the center of the screen.</p>
<p>That’s why we are introducing a <a href="https://stenciljs.com/docs/host-element" rel="noopener noreferrer external">Host element</a> to style the all component conditionally.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Host</span><span style="color:#50FA7B;font-style:italic"> class</span><span style="color:#FF79C6">={</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.match </span><span style="color:#FF79C6">?</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">visible</span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> :</span><span style="color:#BD93F9"> undefined</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">>&#x3C;</span><span style="color:#FF79C6">slot</span><span style="color:#F8F8F2">>Hadouken!&#x3C;/</span><span style="color:#FF79C6">slot</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">Host</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Note that the <code>&lt;Host/&gt;</code> element has to be imported from the <code>@stencil/core</code> .</p>
<p>To style the component we modify its related style <code>./src/components/my-component/my-components.css</code> . We defined our <code>:host</code> , the component, to cover the all screen and we set our message to be displayed in middle of the screen.</p>
<p>Because we are applying the visibility of the message through a class, set or not, on the container we add a related style <code>:host(.visible)</code> to actually display the message.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6;font-style:italic">:</span><span style="color:#50FA7B;font-style:italic">host</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD">    display</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> block</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">    position</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> absolute</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">    top</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">    left</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">    right</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">    bottom</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">    z-index</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 1</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">    visibility</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> hidden</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">    opacity</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">    background</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD"> rgba</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">0.8</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#8BE9FD">    transition</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> opacity </span><span style="color:#BD93F9">250</span><span style="color:#FF79C6">ms</span><span style="color:#BD93F9"> ease-in</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6;font-style:italic">:</span><span style="color:#50FA7B;font-style:italic">host</span><span style="color:#FF79C6">(</span><span style="color:#50FA7B;font-style:italic">.visible</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#8BE9FD">    visibility</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> inherit</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">    opacity</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 1</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">div</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD">    position</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> absolute</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">    top</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 50</span><span style="color:#FF79C6">%</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">    left</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 50</span><span style="color:#FF79C6">%</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">    transform</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD"> translate</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">-50</span><span style="color:#FF79C6">%</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">-50</span><span style="color:#FF79C6">%</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">    color</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> white</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">    font-size</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 4</span><span style="color:#FF79C6">rem</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>If we try out our component again in the browser the result should be a bit more smooth.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*TCSHaGf7dWwmPEDxobD4XA.gif" alt="" / loading="lazy"></p>
<hr />
<h3>Close The Easter Egg</h3>
<p>Fine we have displayed smoothly an easter egg in our application if the Konami code sequence is hit but, as you may have noticed, the message remains open once displayed.</p>
<p>There are several possible way to handle this. One quick solution is a <code>click</code> event on the container which reset our <code>match</code> state.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Host</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">          class</span><span style="color:#FF79C6">={</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.match </span><span style="color:#FF79C6">?</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">visible</span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> :</span><span style="color:#BD93F9"> undefined</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">          onClick</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.match </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> false</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">>&#x3C;</span><span style="color:#FF79C6">slot</span><span style="color:#F8F8F2">>Hadouken!&#x3C;/</span><span style="color:#FF79C6">slot</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">Host</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Just in case, I also suggest to “block” events on the container when not active using style.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6;font-style:italic">:</span><span style="color:#50FA7B;font-style:italic">host</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD">    pointer-events</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> none</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6;font-style:italic">:</span><span style="color:#50FA7B;font-style:italic">host</span><span style="color:#FF79C6">(</span><span style="color:#50FA7B;font-style:italic">.visible</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#8BE9FD">    visibility</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> inherit</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">    opacity</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 1</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>We are now able to close our message with a mouse click.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*ybD_Ot6mbL3MMjuWp2B3RQ.gif" alt="" / loading="lazy"></p>
<hr />
<h3>Altogether</h3>
<p>Altogether our component contains few codes:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Component, h, Listen, State, Host} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@stencil/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  tag</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">konami-code</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  styleUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">my-component.css</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  shadow</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> MyComponent</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  @</span><span style="color:#50FA7B">State</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#F8F8F2"> match</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#FF79C6"> =</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#FF79C6"> readonly</span><span style="color:#F8F8F2"> konamiCode</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">[] </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">ArrowUp</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">ArrowUp</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">ArrowDown</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">ArrowDown</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">ArrowLeft</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">ArrowRight</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">ArrowLeft</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">ArrowRight</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">KeyB</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">KeyA</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  ];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#F8F8F2"> keys</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">[] </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> [];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  @</span><span style="color:#50FA7B">Listen</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">keydown</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, { target</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">document</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> })</span></span>
<span class="line"><span style="color:#50FA7B">  onKeydown</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">$event</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> KeyboardEvent</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.keys.</span><span style="color:#50FA7B">push</span><span style="color:#F8F8F2">($event.code);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.keys.length </span><span style="color:#FF79C6">></span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.konamiCode.length) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">      this</span><span style="color:#F8F8F2">.keys.</span><span style="color:#50FA7B">shift</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.match </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">         this</span><span style="color:#F8F8F2">.konamiCode.</span><span style="color:#50FA7B">join</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">,</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.keys.</span><span style="color:#50FA7B">join</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">,</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Host</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">          class</span><span style="color:#FF79C6">={</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.match </span><span style="color:#FF79C6">?</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">visible</span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> :</span><span style="color:#BD93F9"> undefined</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">          onClick</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.match </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> false</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">>&#x3C;</span><span style="color:#FF79C6">slot</span><span style="color:#F8F8F2">>Hadouken!&#x3C;/</span><span style="color:#FF79C6">slot</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">Host</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Our style is almost as long as our component 😅.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6;font-style:italic">:</span><span style="color:#50FA7B;font-style:italic">host</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD">    display</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> block</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">    position</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> absolute</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">    top</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">    left</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">    right</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">    bottom</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">    z-index</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 1</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">    visibility</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> hidden</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">    opacity</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">    background</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD"> rgba</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">0.8</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#8BE9FD">    transition</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> opacity </span><span style="color:#BD93F9">250</span><span style="color:#FF79C6">ms</span><span style="color:#BD93F9"> ease-in</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">    pointer-events</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> none</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6;font-style:italic">:</span><span style="color:#50FA7B;font-style:italic">host</span><span style="color:#FF79C6">(</span><span style="color:#50FA7B;font-style:italic">.visible</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#8BE9FD">    visibility</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> inherit</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">    opacity</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 1</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">    pointer-events</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> all</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">    cursor</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> pointer</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">div</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD">    position</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> absolute</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">    top</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 50</span><span style="color:#FF79C6">%</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">    left</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 50</span><span style="color:#FF79C6">%</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">    transform</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD"> translate</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">-50</span><span style="color:#FF79C6">%</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">-50</span><span style="color:#FF79C6">%</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">    color</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> white</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">    font-size</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 4</span><span style="color:#FF79C6">rem</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>Bonus</h3>
<p>I also wrote a small component to display to keyboard events for the demo purpose, the first Gif of this article. If interested, here’s its code. Nothing particular regarding what we already have implemented.</p>
<p>The only “tricks” to be aware of are these linked to arrays. If you are manipulating one, you have to create new one to trigger a new call of the function <code>render</code> . Moreover, if it is dynamically rendered, it is safer to set a <code>key</code> attribute to each items.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Component, h, Listen, State} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@stencil/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  tag</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">konami-keys</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  shadow</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> MyKeys</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  @</span><span style="color:#50FA7B">State</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#F8F8F2"> keys</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">[] </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> [];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  @</span><span style="color:#50FA7B">Listen</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">keydown</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, { target</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">document</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> })</span></span>
<span class="line"><span style="color:#50FA7B">  onKeydown</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">$event</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> KeyboardEvent</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.keys </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> [</span><span style="color:#FF79C6">...</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.keys, $event.code];</span></span>
<span class="line"><span style="color:#6272A4">    // 10 being the length of the Konami Code</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.keys.length </span><span style="color:#FF79C6">></span><span style="color:#BD93F9"> 10</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">      this</span><span style="color:#F8F8F2">.keys.</span><span style="color:#50FA7B">shift</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.keys.</span><span style="color:#50FA7B">map</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">key</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">i</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">      return</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FF79C6">span</span><span style="color:#50FA7B;font-style:italic"> key</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">i</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span><span style="color:#FF79C6">{</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">renderKey</span><span style="color:#F8F8F2">(key)</span><span style="color:#FF79C6">}</span><span style="color:#BD93F9">&#x26;nbsp;</span><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">span</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#50FA7B"> renderKey</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">key</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (key</span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">ArrowUp</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">      return</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">⬆️</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">else</span><span style="color:#FF79C6"> if</span><span style="color:#F8F8F2"> (key</span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">ArrowDown</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">      return</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">⬇️</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">else</span><span style="color:#FF79C6"> if</span><span style="color:#F8F8F2"> (key</span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">ArrowLeft</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">      return</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">⬅️</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">else</span><span style="color:#FF79C6"> if</span><span style="color:#F8F8F2"> (key</span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">ArrowRight</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">      return</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">➡️</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">else</span><span style="color:#FF79C6"> if</span><span style="color:#F8F8F2"> (key</span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">KeyB</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">      return</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">🅱️</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">else</span><span style="color:#FF79C6"> if</span><span style="color:#F8F8F2"> (key</span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">KeyA</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">      return</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">🅰️</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">else</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">      return</span><span style="color:#F8F8F2"> key;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>Summary</h3>
<p>I am aware none of these 35 daily blog posts have helped or will help solved the current crisis. However, I hope that maybe they might help someone, somewhere, someday.</p>
<p>Stay home, stay safe!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Currency Picker And Formatter With Ionic React]]></title>
          <description><![CDATA[Build a currency picker and formatter for your application developed with Ionic and React]]></description>
          <link>https://daviddalbusco.com/blog/currency-picker-and-formatter-with-ionic-react/</link>
          <pubDate>Sat, 18 Apr 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*vNyq4W-5NQoeX5jo8TqBQw.png" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@pawelj?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Pawel Janiak</a> on <a href="https://unsplash.com/s/photos/free?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I share <a href="https://daviddalbusco.com/blog/how-to-call-the-service-worker-from-the-web-app-context" rel="noopener noreferrer external">one trick a day</a> until the original scheduled date of the end of the COVID-19 quarantine in Switzerland, April 19th 2020. <strong>One</strong> days left until this first milestone. Hopefully better days are ahead.</p>
<hr />
<p>I was looking for a subject idea for today’s blog post and it came to my mind that I could maybe share something I learned with <a href="https://tietracker.app.link/" rel="noopener noreferrer external">Tie Tracker</a> ⏱️, a simple, open source and free time tracking app I have developed with <a href="https://ionicframework.com/" rel="noopener noreferrer external">Ionic</a> and <a href="https://reactjs.org/" rel="noopener noreferrer external">React</a> again.</p>
<p>That’s why I’m sharing with you my solution to develop a custom currency picker and formatter.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*YCPfr5rf-N4Tk9l9JusTXA.gif" alt="" / loading="lazy"></p>
<hr />
<h3>Start</h3>
<p>If you don’t have an Ionic React application yet, you can follow this tutorial by creating a sample one using their CLI.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">ionic</span><span style="color:#F1FA8C"> start</span></span></code></pre><p>When prompted, select “React”, your application name and for example the template “blank”.</p>
<hr />
<h3>List Of Currencies</h3>
<p>We intend to develop a custom currency picker, that’s why we need, a list of currencies. For such purpose, we can download the one provided on the <a href="https://github.com/xsolla/currency-format" rel="noopener noreferrer external">Xsolla</a> repo as it is free and licensed under MIT license.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">curl</span><span style="color:#F1FA8C"> https://raw.githubusercontent.com/xsolla/currency-format/master/currency-format.json</span><span style="color:#BD93F9"> -o</span><span style="color:#F1FA8C"> public/assets/currencies.json</span></span></code></pre><p>I use <code>curl</code> because I am using a Macbook but what does matter is to save the list of currencies in the assets folder as it will have to be shipped with the app.</p>
<hr />
<h3>TypeScript Definitions</h3>
<p>We are going to need a TypeScript definitions to handle the list we just downloaded. That’s why we create following interfaces in <code>./src/definitions/currency.d.ts</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> interface</span><span style="color:#8BE9FD;font-style:italic"> Currency</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    name</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    fractionSize</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    symbol</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        grapheme</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        template</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        rtl</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"><span style="color:#F8F8F2">    uniqSymbol</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> interface</span><span style="color:#8BE9FD;font-style:italic"> Currencies</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    [</span><span style="color:#FFB86C;font-style:italic">currency</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">]</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Currency</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Note that I am not sure that using a subfolder <code>definitions</code> is really the best practice, it is just something I do. Do not think it matters that much, I just like to split my code in, kind of, packages.</p>
<hr />
<h3>Modal: Currency Picker</h3>
<p>To develop our picker I suggest that we use a modal. It should display the list of available currencies (currency name and abbreviation), allow the user to filter these and ultimately let him/her select one.</p>
<p>We create a new component <code>./src/components/CurrenciesModal.tsx</code> which receive as properties the current selected currency and a function to close the modal and pass the user selection.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">interface</span><span style="color:#8BE9FD;font-style:italic"> Props</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    closeAction</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Function</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    currency</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>It contains also two states. The list of currencies and a filtered one, which is, when component mounted, equals to the all list.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> [currencies, setCurrencies] </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> (useState </span><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2"> Currencies) </span><span style="color:#FF79C6">|</span><span style="color:#F8F8F2"> (</span><span style="color:#BD93F9">undefined</span><span style="color:#FF79C6"> ></span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> [filteredCurrencies, setFilteredCurrencies] </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#F8F8F2">    (useState </span><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2"> Currencies) </span><span style="color:#FF79C6">|</span><span style="color:#F8F8F2"> (</span><span style="color:#BD93F9">undefined</span><span style="color:#FF79C6"> ></span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">);</span></span></code></pre><p>To initiate these we use <code>useEffect</code> hooks and we read the JSON data we downloaded before.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">useEffect</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    initCurrencies</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#6272A4">    // eslint-disable-next-line react-hooks/exhaustive-deps</span></span>
<span class="line"><span style="color:#F8F8F2">}, []);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">useEffect</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    setFilteredCurrencies</span><span style="color:#F8F8F2">(currencies);</span></span>
<span class="line"><span style="color:#F8F8F2">}, [currencies]);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> initCurrencies</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> res</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Response</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#FF79C6">                   await</span><span style="color:#50FA7B"> fetch</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">./assets/currencies.json</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">res) {</span></span>
<span class="line"><span style="color:#50FA7B">            setCurrencies</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">undefined</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> currencies</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Currencies</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> res.</span><span style="color:#50FA7B">json</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">        setCurrencies</span><span style="color:#F8F8F2">(currencies);</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#50FA7B">        setCurrencies</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">undefined</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>To proceed with filtering, we implement a function which read the user inputs and call another one which effectively takes care of applying a filter on the list we maintain as state objects.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> onFilter</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">$event</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> CustomEvent</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">KeyboardEvent</span><span style="color:#F8F8F2">>) {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">$event) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> input</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ($event.target </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> InputTargetEvent</span><span style="color:#F8F8F2">).value;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">input </span><span style="color:#FF79C6">||</span><span style="color:#F8F8F2"> input </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> undefined</span><span style="color:#FF79C6"> ||</span><span style="color:#F8F8F2"> input </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> ''</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#50FA7B">        setFilteredCurrencies</span><span style="color:#F8F8F2">(currencies);</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">else</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> filtered</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Currencies</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#FF79C6">                        await</span><span style="color:#50FA7B"> filterCurrencies</span><span style="color:#F8F8F2">(input);</span></span>
<span class="line"><span style="color:#50FA7B">        setFilteredCurrencies</span><span style="color:#F8F8F2">(filtered);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Finally we implement our modal’s GUI which contains a <code>searchbar</code> and a <code>list</code> of <code>items</code> , the currencies.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonSearchbar</span><span style="color:#50FA7B;font-style:italic"> debounce</span><span style="color:#FF79C6">={</span><span style="color:#BD93F9">500</span><span style="color:#FF79C6">}</span><span style="color:#50FA7B;font-style:italic"> placeholder</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Filter</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">              onIonInput</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">$event</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> CustomEvent</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">KeyboardEvent</span><span style="color:#F8F8F2">>) </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> onFilter</span><span style="color:#F8F8F2">($event)</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonSearchbar</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonList</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonRadioGroup</span><span style="color:#50FA7B;font-style:italic"> value</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">props.currency</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#FF79C6">        {</span><span style="color:#50FA7B">renderCurrencies</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonRadioGroup</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonList</span><span style="color:#F8F8F2">></span></span></code></pre><p>Altogether our component looks like the following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> React, {useEffect, useState} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    IonList,</span></span>
<span class="line"><span style="color:#F8F8F2">    IonItem,</span></span>
<span class="line"><span style="color:#F8F8F2">    IonToolbar,</span></span>
<span class="line"><span style="color:#F8F8F2">    IonRadioGroup,</span></span>
<span class="line"><span style="color:#F8F8F2">    IonLabel,</span></span>
<span class="line"><span style="color:#F8F8F2">    IonRadio,</span></span>
<span class="line"><span style="color:#F8F8F2">    IonSearchbar,</span></span>
<span class="line"><span style="color:#F8F8F2">    IonContent,</span></span>
<span class="line"><span style="color:#F8F8F2">    IonTitle,</span></span>
<span class="line"><span style="color:#F8F8F2">    IonHeader, IonButtons, IonButton, IonIcon</span></span>
<span class="line"><span style="color:#F8F8F2">} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@ionic/react</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {close} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">ionicons/icons</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Currencies} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">../definitions/currency</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">interface</span><span style="color:#8BE9FD;font-style:italic"> Props</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    closeAction</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Function</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    currency</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">interface</span><span style="color:#8BE9FD;font-style:italic"> InputTargetEvent</span><span style="color:#FF79C6"> extends</span><span style="color:#8BE9FD;font-style:italic"> EventTarget</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> CurrenciesModal</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> React</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">FC</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Props</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">props</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Props</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> [currencies, setCurrencies] </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#50FA7B">          useState</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Currencies</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">>(</span><span style="color:#BD93F9">undefined</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> [filteredCurrencies, setFilteredCurrencies] </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#50FA7B">          useState</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Currencies</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">>(</span><span style="color:#BD93F9">undefined</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    useEffect</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">        initCurrencies</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#6272A4">        // eslint-disable-next-line react-hooks/exhaustive-deps</span></span>
<span class="line"><span style="color:#F8F8F2">    }, []);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    useEffect</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">        setFilteredCurrencies</span><span style="color:#F8F8F2">(currencies);</span></span>
<span class="line"><span style="color:#F8F8F2">    }, [currencies]);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> initCurrencies</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">            const</span><span style="color:#F8F8F2"> res</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Response</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#FF79C6">                  await</span><span style="color:#50FA7B"> fetch</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">./assets/currencies.json</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">            if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">res) {</span></span>
<span class="line"><span style="color:#50FA7B">                setCurrencies</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">undefined</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">                return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">            const</span><span style="color:#F8F8F2"> currencies</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Currencies</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> res.</span><span style="color:#50FA7B">json</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">            setCurrencies</span><span style="color:#F8F8F2">(currencies);</span></span>
<span class="line"><span style="color:#F8F8F2">        } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#50FA7B">            setCurrencies</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">undefined</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> onFilter</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">$event</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> CustomEvent</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">KeyboardEvent</span><span style="color:#F8F8F2">>) {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">$event) {</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> input</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#F8F8F2">              ($event.target </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> InputTargetEvent</span><span style="color:#F8F8F2">).value;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">input </span><span style="color:#FF79C6">||</span><span style="color:#F8F8F2"> input </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> undefined</span><span style="color:#FF79C6"> ||</span><span style="color:#F8F8F2"> input </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> ''</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#50FA7B">            setFilteredCurrencies</span><span style="color:#F8F8F2">(currencies);</span></span>
<span class="line"><span style="color:#F8F8F2">        } </span><span style="color:#FF79C6">else</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">            const</span><span style="color:#F8F8F2"> filtered</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Currencies</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#FF79C6">                  await</span><span style="color:#50FA7B"> filterCurrencies</span><span style="color:#F8F8F2">(input);</span></span>
<span class="line"><span style="color:#50FA7B">            setFilteredCurrencies</span><span style="color:#F8F8F2">(filtered);</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> filterCurrencies</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">filter</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">                   Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Currencies</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">currencies) {</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> results</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Currencies</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> Object.</span><span style="color:#50FA7B">keys</span><span style="color:#F8F8F2">(currencies)</span></span>
<span class="line"><span style="color:#F8F8F2">            .</span><span style="color:#50FA7B">filter</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">key</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">                return</span><span style="color:#F8F8F2"> ((key.</span><span style="color:#50FA7B">toLowerCase</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">indexOf</span><span style="color:#F8F8F2">(filter.</span><span style="color:#50FA7B">toLowerCase</span><span style="color:#F8F8F2">()) </span><span style="color:#FF79C6">></span><span style="color:#FF79C6"> -</span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">||</span></span>
<span class="line"><span style="color:#F8F8F2">                    (currencies[key].name </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> currencies[key].name.</span><span style="color:#50FA7B">toLowerCase</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">indexOf</span><span style="color:#F8F8F2">(filter.</span><span style="color:#50FA7B">toLowerCase</span><span style="color:#F8F8F2">()) </span><span style="color:#FF79C6">></span><span style="color:#FF79C6"> -</span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">));</span></span>
<span class="line"><span style="color:#F8F8F2">            })</span></span>
<span class="line"><span style="color:#F8F8F2">            .</span><span style="color:#50FA7B">reduce</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">obj</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Currencies</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">key</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">                obj[key] </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> currencies[key];</span></span>
<span class="line"><span style="color:#FF79C6">                return</span><span style="color:#F8F8F2"> obj;</span></span>
<span class="line"><span style="color:#F8F8F2">            }, {});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> results;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonHeader</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonToolbar</span><span style="color:#50FA7B;font-style:italic"> color</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">primary</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonTitle</span><span style="color:#F8F8F2">>Picker&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonTitle</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonButtons</span><span style="color:#50FA7B;font-style:italic"> slot</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">start</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonButton</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                          onClick</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> props.</span><span style="color:#50FA7B">closeAction</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                          &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonIcon</span><span style="color:#50FA7B;font-style:italic"> icon</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">close</span><span style="color:#FF79C6">}</span><span style="color:#50FA7B;font-style:italic"> slot</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">icon-only</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                          &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonIcon</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                        &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonButton</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonButtons</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonToolbar</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonHeader</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonContent</span><span style="color:#50FA7B;font-style:italic"> className</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ion-padding</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonSearchbar</span><span style="color:#50FA7B;font-style:italic"> debounce</span><span style="color:#FF79C6">={</span><span style="color:#BD93F9">500</span><span style="color:#FF79C6">}</span><span style="color:#50FA7B;font-style:italic"> placeholder</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Filter</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                 onIonInput</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">$event</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> CustomEvent</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">KeyboardEvent</span><span style="color:#F8F8F2">>) </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> onFilter</span><span style="color:#F8F8F2">($event)</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonSearchbar</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonList</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonRadioGroup</span><span style="color:#50FA7B;font-style:italic"> value</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">props.currency</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#FF79C6">                        {</span><span style="color:#50FA7B">renderCurrencies</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonRadioGroup</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonList</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonContent</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/></span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    function</span><span style="color:#50FA7B"> renderCurrencies</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">filteredCurrencies</span></span>
<span class="line"><span style="color:#FF79C6">            ||</span><span style="color:#F8F8F2"> filteredCurrencies </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> Object.</span><span style="color:#50FA7B">keys</span><span style="color:#F8F8F2">(filteredCurrencies)</span></span>
<span class="line"><span style="color:#F8F8F2">                     .</span><span style="color:#50FA7B">map</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">key</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonItem</span><span style="color:#50FA7B;font-style:italic"> key</span><span style="color:#FF79C6">={</span><span style="color:#F1FA8C">`</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">key</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                            onClick</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> props.</span><span style="color:#50FA7B">closeAction</span><span style="color:#F8F8F2">(key)</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonLabel</span><span style="color:#F8F8F2">></span><span style="color:#FF79C6">{</span><span style="color:#F8F8F2">filteredCurrencies[key].name</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">{</span><span style="color:#F8F8F2">key</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonLabel</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonRadio</span><span style="color:#50FA7B;font-style:italic"> value</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">key</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">/></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonItem</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> CurrenciesModal;</span></span></code></pre><hr />
<h3>Page: Home</h3>
<p>Our picker being ready, we can now use it. For such purpose we integrate it to the main page of our application, the <code>home</code> page. We are also adding a state to display the current selected currency which I initialized with <code>CHF</code> as it is the currency of Switzerland.</p>
<p>Moreover, we are also implementing a function to update the currency according the one the user would pick using our above modal.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> React, {useState} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {IonContent, IonHeader, IonPage, IonTitle, IonToolbar, IonModal, IonButton, IonLabel} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@ionic/react</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> CurrenciesModal </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">../components/CurrenciesModal</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> Home</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> React</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">FC</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> [currency, setCurrency] </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useState</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">>(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">CHF</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> [showModal, setShowModal] </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useState</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">boolean</span><span style="color:#F8F8F2">>(</span><span style="color:#BD93F9">false</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    function</span><span style="color:#50FA7B"> updateCurrency</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">currency</span><span style="color:#FF79C6">?:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#50FA7B">        setShowModal</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">false</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">currency) {</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">        setCurrency</span><span style="color:#F8F8F2">(currency);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonPage</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonHeader</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonToolbar</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonTitle</span><span style="color:#F8F8F2">>Home&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonTitle</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonToolbar</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonHeader</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonContent</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonModal</span><span style="color:#50FA7B;font-style:italic"> isOpen</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">showModal</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                 onDidDismiss</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> setShowModal</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">false</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">CurrenciesModal</span><span style="color:#50FA7B;font-style:italic"> currency</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">currency</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                                     closeAction</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">updateCurrency</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">CurrenciesModal</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonModal</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">>123.45 </span><span style="color:#FF79C6">{</span><span style="color:#F8F8F2">currency</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonButton</span><span style="color:#50FA7B;font-style:italic"> onClick</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> setShowModal</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">true</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonLabel</span><span style="color:#F8F8F2">>Pick currency&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonLabel</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonButton</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonContent</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonPage</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> Home;</span></span></code></pre><p>If you implemented the above code you should now be able to run the application and pick currencies.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*l7-IXroNBIyWj11Ho1h9gA.gif" alt="" / loading="lazy"></p>
<hr />
<h3>Format Currency</h3>
<p>Being able to select a currency is nice, but being able to use it is even better 😉.</p>
<p>To format our amount, we are going to use the standard built-in object <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat" rel="noopener noreferrer external">Intl.NumberFormat</a> which is now pretty well <a href="https://caniuse.com/#search=NumberFormat" rel="noopener noreferrer external">supported</a> by any browser.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">function</span><span style="color:#50FA7B"> formatCurrency</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (currency </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#F8F8F2"> Intl.</span><span style="color:#50FA7B">NumberFormat</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">fr</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">).</span><span style="color:#50FA7B">format</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#F8F8F2"> Intl.</span><span style="color:#50FA7B">NumberFormat</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">fr</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">           { style</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">currency</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, currency</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> currency }).</span><span style="color:#50FA7B">format</span><span style="color:#F8F8F2">(value);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Note that in the above function I hardcoded french as it is my mother tongue. This can be replaced by the one of your choice or if you are using <a href="https://www.i18next.com/" rel="noopener noreferrer external">i18next</a> with the following dynamic language.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> i18n </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">i18next</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">function</span><span style="color:#50FA7B"> formatCurrency</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (currency </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#F8F8F2"> Intl.</span><span style="color:#50FA7B">NumberFormat</span><span style="color:#F8F8F2">(i18n.language).</span><span style="color:#50FA7B">format</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#F8F8F2"> Intl.</span><span style="color:#50FA7B">NumberFormat</span><span style="color:#F8F8F2">(i18n.language,</span></span>
<span class="line"><span style="color:#F8F8F2">           { style</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">currency</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, currency</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> currency }).</span><span style="color:#50FA7B">format</span><span style="color:#F8F8F2">(value);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Finally, we are replacing the static display of the value <code>123.45 {currency}</code> with the function’s call.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">></span><span style="color:#FF79C6">{</span><span style="color:#50FA7B">formatCurrency</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">123.45</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">></span></span></code></pre><p>Altogether our main page now should contain the following code:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> React, {useState} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {IonContent, IonHeader, IonPage, IonTitle, IonToolbar, IonModal, IonButton, IonLabel} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@ionic/react</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> CurrenciesModal </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">../components/CurrenciesModal</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> Home</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> React</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">FC</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> [currency, setCurrency] </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useState</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">>(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">CHF</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> [showModal, setShowModal] </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useState</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">boolean</span><span style="color:#F8F8F2">>(</span><span style="color:#BD93F9">false</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    function</span><span style="color:#50FA7B"> updateCurrency</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">currency</span><span style="color:#FF79C6">?:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#50FA7B">        setShowModal</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">false</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">currency) {</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">        setCurrency</span><span style="color:#F8F8F2">(currency);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    function</span><span style="color:#50FA7B"> formatCurrency</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (currency </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#F8F8F2"> Intl.</span><span style="color:#50FA7B">NumberFormat</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">fr</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">).</span><span style="color:#50FA7B">format</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#F8F8F2"> Intl.</span><span style="color:#50FA7B">NumberFormat</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">fr</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">           { style</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">currency</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, currency</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> currency }).</span><span style="color:#50FA7B">format</span><span style="color:#F8F8F2">(value);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonPage</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonHeader</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonToolbar</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonTitle</span><span style="color:#F8F8F2">>Home&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonTitle</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonToolbar</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonHeader</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonContent</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonModal</span><span style="color:#50FA7B;font-style:italic"> isOpen</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">showModal</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                          onDidDismiss</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> setShowModal</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">false</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">CurrenciesModal</span><span style="color:#50FA7B;font-style:italic"> currency</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">currency</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                                     closeAction</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">updateCurrency</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">CurrenciesModal</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonModal</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">></span><span style="color:#FF79C6">{</span><span style="color:#50FA7B">formatCurrency</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">123.45</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonButton</span><span style="color:#50FA7B;font-style:italic"> onClick</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> setShowModal</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">true</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonLabel</span><span style="color:#F8F8F2">>Pick currency&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonLabel</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonButton</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonContent</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonPage</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> Home;</span></span></code></pre><p>Voilà, both our currency picker and formatter are implemented in our Ionic React application 🎉.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*6ugZ2Y2mcpHkmdS4gpNJ6w.gif" alt="" / loading="lazy"></p>
<hr />
<h3>Summary</h3>
<p>Ionic and React together are really fun. Checkout <a href="https://tietracker.app.link" rel="noopener noreferrer external">Tie Tracker</a> and of course your <a href="https://github.com/peterpeterparker/tietracker" rel="noopener noreferrer external">Pull Requests</a> to improve the app are most welcomed 😁.</p>
<p>Stay home, stay safe!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[An Open Source Medium Like WYSIWYG Editor]]></title>
          <description><![CDATA[Add our WYSIWYG floating or sticky editor to any web applications]]></description>
          <link>https://daviddalbusco.com/blog/an-open-source-medium-like-wysiwyg-editor/</link>
          <pubDate>Fri, 17 Apr 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*mEgbLcMRBUaJIvwIdirEvg.png" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@silviana?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Silviana Toader</a> on <a href="https://unsplash.com/s/photos/free?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I share <a href="https://daviddalbusco.com/blog/how-to-call-the-service-worker-from-the-web-app-context" rel="noopener noreferrer external">one trick a day</a> until the original scheduled date of the end of the COVID-19 quarantine in Switzerland, April 19th 2020. <strong>Two</strong> days left until this first milestone. Hopefully better days are ahead.</p>
<hr />
<p>For <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a> our editor for presentations, we have developed many custom made open source Web Components developed with <a href="https://stenciljs.com/" rel="noopener noreferrer external">Stencil</a>.</p>
<p>One of these is a Medium Like WYSIWYG editor. It can be integrated in any modern web applications, regardless of its technologies, and works on any devices (desktop, tablet and mobile).</p>
<p><img src="https://daviddalbusco.com/assets/images/1*Dgkj18MA4FGYsd1FmYSegA.gif" alt="" / loading="lazy"></p>
<p>I am actually not sure if I ever shared this component or not but as I spent my day improving its layout, bringing it some attention (love) it well deserved, and release a new version, that’s why I’m sharing it with you with this new blog post 😉.</p>
<hr />
<h3>Installation</h3>
<p>We are providing some guidance in our <a href="https://docs.deckdeckgo.com/components/inline-editor" rel="noopener noreferrer external">documentation</a> and Stencil is also displaying how any components can be installed in with any <a href="https://stenciljs.com/docs/overview" rel="noopener noreferrer external">frameworks</a>.</p>
<hr />
<h4>Install From A CDN</h4>
<p>To fetch the component from a CDN, as for example <a href="https://unpkg.com/" rel="noopener noreferrer external">Unpkg</a>, add the following to the header of your HTML.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">script</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    type</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">module</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://unpkg.com/@deckdeckgo/inline-editor@latest/dist/deckdeckgo-inline-editor/deckdeckgo-inline-editor.esm.js</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">script</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    nomodule</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">""</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://unpkg.com/@deckdeckgo/inline-editor@latest/dist/deckdeckgo-inline-editor/deckdeckgo-inline-editor.js</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span></code></pre><hr />
<h4>Install From NPM</h4>
<p>To install the project from <a href="https://www.npmjs.com/" rel="noopener noreferrer external">npm</a>, run the following command in your terminal:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> install</span><span style="color:#F1FA8C"> @deckdeckgo/inline-editor</span></span></code></pre><p>According your need, either import it:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@deckdeckgo/inline-editor</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span></code></pre><p>Or use a custom loader:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { </span><span style="color:#FFB86C;font-style:italic">defineCustomElements</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> deckDeckGoElement } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@deckdeckgo/inline-editor/dist/loader</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">deckDeckGoElement</span><span style="color:#F8F8F2">();</span></span></code></pre><hr />
<h3>Add The Component To Your Application</h3>
<p>I like when component’s usage is dead simple. To add it to your application, “just” add it to the DOM and you are good to go.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">deckgo-inline-editor</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">deckgo-inline-editor</span><span style="color:#F8F8F2">></span></span></code></pre><p>That’s it, you have added a WYSIWYG editor to your application 🎉.</p>
<hr />
<h3>Editable Elements</h3>
<p>Per default, the component will make any elements <code>h1</code> , <code>h2</code> , <code>h3</code> , <code>h4</code> , <code>h5</code> , <code>h6</code> and <code>div</code> for which the attribute <code>contenteditable</code> is defined editable.</p>
<p>If like us with our editor, you would have other need, for example we handle content in <code>section</code> , you can override this list with the help of the property <code>containers</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">deckgo-inline-editor</span><span style="color:#50FA7B;font-style:italic"> containers</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">h1,h2,section</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">deckgo-inline-editor</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">/</span><span style="color:#F8F8F2">></span></span></code></pre><p>As you can notice, as soon as I do so, the following paragraphs ( <code>p</code> ) are not editable anymore even though they are still set as <code>contenteditable</code> .</p>
<p><img src="https://daviddalbusco.com/assets/images/1*olJ2RT5ChCBiF6UP4ZBNSw.gif" alt="" / loading="lazy"></p>
<hr />
<h3>Container Element</h3>
<p>You may like to allow the interaction only with a specific part of your application and not the all document. For such purpose, it does also expose a property <code>attachTo</code> . If you would provide it, then only mouse or touch events coming from that particular container are going to be considered.</p>
<hr />
<h3>Mobile Devices</h3>
<p>On mobile devices, it might be not the best UX to use a floating editor. When users are selecting text on their devices, a default list of system OS options (copy, paste, etc.) are automatically displayed which might conflicts with the editor.</p>
<p>That’s why we had the idea to make optionally the editor sticky on mobile devices.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">deckgo-inline-editor</span><span style="color:#50FA7B;font-style:italic"> containers</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">h1,h2,section</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> sticky-mobile</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">true</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">> &#x3C;/</span><span style="color:#FF79C6">deckgo-inline-editor</span><span style="color:#F8F8F2">></span></span></code></pre><p>Note that the editor is displayed at the top on iOS and bottom on Android. I rather like this last version but I did not find a clever way to solve this on iOS as the Webview is not resized and the keyboard size is not accessible.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*yOasEsPf8xmnmrB1TMsMXg.gif" alt="" / loading="lazy"></p>
<p>Also worth to notice, the component emit an event <code>stickyToolbarActivated</code> when the toolbar is displayed. Thanks to this event, you can for example hide the header or footer of your applications, avoiding a small design glitch of two layers.</p>
<hr />
<h3>List And Alignment</h3>
<p>We recently added the ability to modify the alignment, thanks to a Pull Request provided by <a href="https://twitter.com/BoradAkash" rel="noopener noreferrer external">Akash Borad</a>. Not all heroes wear capes!</p>
<p>Even though, in our editor, we don’t use these as we are offering these two options in our main toolbar. That’s why the component exposes two properties, <code>list</code> and <code>align</code> , in case you would also not use these.</p>
<p>Likewise, images are not taken in account by the component per default. This can be modified with the help of another property, <code>img-editable</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">deckgo-inline-editor</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    containers</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">h1,h2,section</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    sticky-mobile</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">true</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    list</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">false</span><span style="color:#E9F284">"</span></span>
<span class="line"><span>    </span><span style="color:#F8F8F2;font-style:italic;text-decoration:underline">align</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">false</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    img-editable</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">true</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">deckgo-inline-editor</span><span style="color:#F8F8F2">></span></span></code></pre><p><img src="https://daviddalbusco.com/assets/images/1*2EankUNvUC5y5p1bqZAqSQ.gif" alt="" / loading="lazy"></p>
<hr />
<h3>Colors</h3>
<p>The colors can be modified with the help of our custom made color picker. It has a default palette but it can be overwritten too with its corresponding property <code>palette</code> .</p>
<p><img src="https://daviddalbusco.com/assets/images/1*eZl2B801qCtpxE9rmL3Iuw.gif" alt="" / loading="lazy"></p>
<hr />
<h3>RTL</h3>
<p>If your application’s direction is Right-To-Left the component ordering will remains the same, but, automatically, the alignment feature will notice it and will be displayed as such per default.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*CXis2-sk0Na3xKIrMCtZpQ.gif" alt="" / loading="lazy"></p>
<hr />
<h3>CSS Customization</h3>
<p>I did not count but many CSS variables are available to style the component, notably everything which has to do with colors, backgrounds and selection.</p>
<p>These are displayed in our <a href="https://docs.deckdeckgo.com/components/inline-editor" rel="noopener noreferrer external">documentation</a>.</p>
<p>As always, if something is missing or if you would need a feature, ping us on <a href="https://github.com/deckgo/deckdeckgo/" rel="noopener noreferrer external">GitHub</a>. Furthermore, Pull Requests are most welcomed 😁.</p>
<hr />
<h3>And More…</h3>
<p>There are more options, as being able to provide a custom action or make the component also sticky on desktop.</p>
<hr />
<h3>Summary</h3>
<p>To speak frankly, this is probably the most complicated component I ever had to develop. I think the fact that it is shadowed and that the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Selection" rel="noopener noreferrer external">Selection API</a> is not yet that friendly with it did not make things easier. Even though I am happy with the results, it works well in our <a href="https://deckdeckgo.com" rel="noopener noreferrer external">editor</a> and I hope it can someday be useful to someone somewhere too.</p>
<p>Stay home, stay safe!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Git Commands I Always Forget]]></title>
          <description><![CDATA[Revert last commit, change last or multiple commit messages or delete tags]]></description>
          <link>https://daviddalbusco.com/blog/git-commands-i-always-forget/</link>
          <pubDate>Thu, 16 Apr 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*9xilCpCgG-BqhOICl-zNCg.png" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@jonny_lew?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Jonatan Lewczuk</a> on <a href="https://unsplash.com/s/photos/free?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I share <a href="https://daviddalbusco.com/blog/how-to-call-the-service-worker-from-the-web-app-context" rel="noopener noreferrer external">one trick a day</a> until the original scheduled date of the end of the COVID-19 quarantine in Switzerland, April 19th 2020. <strong>Three</strong> days left until this first milestone. Hopefully better days are ahead.</p>
<hr />
<p>When it comes to <a href="https://git-scm.com/" rel="noopener noreferrer external">Git</a>, I probably rely too much on its really well-made integration of my editor, <a href="https://www.jetbrains.com/webstorm/" rel="noopener noreferrer external">WebStorm</a>. This has for consequence, that I sometimes forget useful commands, even simple ones, which I only have rarely to run in my terminal.</p>
<p>That’s why I am sharing this blog post mostly for my future self 😅.</p>
<hr />
<h3>Revert Last Commit</h3>
<p>To revert your last commit if it has not been pushed yet, you can rewind the history from a step back with the help of the <code>reset</code> command.</p>
<p>However, if your commit has already been pushed, you can preserve your history and run a <code>revert</code> command (where <code>eec47301</code> is the revision number of the commit to revert which can be found for example with the help of the <code>git log</code> command) followed by a commit and push.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">git</span><span style="color:#F1FA8C"> revert</span><span style="color:#F1FA8C"> eec47301</span></span></code></pre><p>Alternatively revert it without preserving the history with the help of the <code>reset</code> command and the option <code>--hard</code> followed by a push with the option <code>--force</code>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">git</span><span style="color:#F1FA8C"> reset</span><span style="color:#BD93F9"> --hard</span><span style="color:#F1FA8C"> eec47301</span></span></code></pre><p>Needless to say, it has to be use wisely.</p>
<hr />
<h3>Change Last Commit Message</h3>
<p>If your last commit message was wrong or if you had for example a typo, you can modify the last, or most recent, commit message with the option <code>--amend</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">git</span><span style="color:#F1FA8C"> commit</span><span style="color:#BD93F9"> --amend</span></span></code></pre><p>If your commit has not been pushed yet, nothing else to do. To the contrary, if you already have pushed it, you can update your repo with <code>--force</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">git</span><span style="color:#F1FA8C"> push</span><span style="color:#BD93F9"> --force</span></span></code></pre><hr />
<h3>Change Multiple Commit Messages</h3>
<p>If you are looking to amend multiple commit messages, which might happens for example if you did forget to specify the related issue number, you can proceed with the help of <code>rebase</code> .</p>
<p>Credits for this solution goes to these provided by <a href="https://jacopretorius.net/2013/05/amend-multiple-commit-messages-with-git.html" rel="noopener noreferrer external">Jaco Pretorius</a> and <a href="https://linuxize.com/post/change-git-commit-message/" rel="noopener noreferrer external">Linuxize</a>. Not all heroes wear capes!</p>
<p>To start amending we run the following command where <code>2</code> is the number of commits on which we want to perform a <code>rebase</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">git</span><span style="color:#F1FA8C"> rebase</span><span style="color:#BD93F9"> -i</span><span style="color:#F1FA8C"> HEAD~2</span></span></code></pre><p>This will open a prompt which will allow us to specify changes on each commits.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">pick</span><span style="color:#F1FA8C"> e68a142</span><span style="color:#F1FA8C"> my</span><span style="color:#F1FA8C"> frst</span><span style="color:#F1FA8C"> update</span></span>
<span class="line"><span style="color:#50FA7B">pick</span><span style="color:#F1FA8C"> 1613f1e</span><span style="color:#F1FA8C"> my</span><span style="color:#F1FA8C"> scnd</span><span style="color:#F1FA8C"> update</span></span></code></pre><p>As we want to change the commit message, we modify <code>pick</code> with <code>reword</code> (or the short version <code>r</code> ).</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">reword</span><span style="color:#F1FA8C"> e68a142</span><span style="color:#F1FA8C"> my</span><span style="color:#F1FA8C"> frst</span><span style="color:#F1FA8C"> update</span></span>
<span class="line"><span style="color:#50FA7B">reword</span><span style="color:#F1FA8C"> 1613f1e</span><span style="color:#F1FA8C"> my</span><span style="color:#F1FA8C"> scnd</span><span style="color:#F1FA8C"> update</span></span></code></pre><p>Once done, we save ( <code>:wq</code> ) and the prompt will automatically guide us to the first selected commit we would like to change.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">my</span><span style="color:#F1FA8C"> frst</span><span style="color:#F1FA8C"> update</span></span></code></pre><p>We can correct the message (<code>x</code> to delete a character, <code>i</code>s to switch to insert mode, <code>a</code>a to append and always <code>Esc</code> escape editing mode), save it and the prompt will again automatically guide us to the next commit message we would like to change and so on.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">my</span><span style="color:#F1FA8C"> snd</span><span style="color:#F1FA8C"> update</span></span></code></pre><p>Once finished, our terminal will look like the following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">❯</span><span style="color:#F1FA8C"> git</span><span style="color:#F1FA8C"> rebase</span><span style="color:#BD93F9"> -i</span><span style="color:#F1FA8C"> HEAD~2</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">[detached HEAD 1f02610] my first update</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">Date:</span><span style="color:#F1FA8C"> Thu</span><span style="color:#F1FA8C"> Apr</span><span style="color:#BD93F9"> 16</span><span style="color:#F1FA8C"> 15:55:09</span><span style="color:#BD93F9"> 2020</span><span style="color:#F1FA8C"> +0200</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">1</span><span style="color:#F1FA8C"> file</span><span style="color:#F1FA8C"> changed,</span><span style="color:#BD93F9"> 1</span><span style="color:#F1FA8C"> insertion</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">+</span><span style="color:#F8F8F2">)</span><span style="color:#F1FA8C">,</span><span style="color:#BD93F9"> 1</span><span style="color:#F1FA8C"> deletion</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">-</span><span style="color:#F8F8F2">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">[detached HEAD 68d3edd] my second update</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">Date:</span><span style="color:#F1FA8C"> Thu</span><span style="color:#F1FA8C"> Apr</span><span style="color:#BD93F9"> 16</span><span style="color:#F1FA8C"> 16:00:29</span><span style="color:#BD93F9"> 2020</span><span style="color:#F1FA8C"> +0200</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">1</span><span style="color:#F1FA8C"> file</span><span style="color:#F1FA8C"> changed,</span><span style="color:#BD93F9"> 4</span><span style="color:#F1FA8C"> insertions</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">+</span><span style="color:#F8F8F2">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">Successfully</span><span style="color:#F1FA8C"> rebased</span><span style="color:#F1FA8C"> and</span><span style="color:#F1FA8C"> updated</span><span style="color:#F1FA8C"> refs/heads/master.</span></span></code></pre><p>At this point our history is locally rewritten but not yet updated in our repo, that’s why we push these.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">git</span><span style="color:#F1FA8C"> push</span><span style="color:#BD93F9"> --force</span></span></code></pre><hr />
<h3>Abort Rebase</h3>
<p>If you would face problems while running the above process, you would be able to cancel the rebase operation using <code>--abort</code>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">git</span><span style="color:#F1FA8C"> rebase</span><span style="color:#BD93F9"> --abort</span></span></code></pre><hr />
<h3>Abort Merge</h3>
<p>Speaking of abort, it is also possible to quit a merge while using the same option.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">git</span><span style="color:#F1FA8C"> merge</span><span style="color:#BD93F9"> --abort</span></span></code></pre><hr />
<h3>Delete A Tag</h3>
<p>When you delete a release on <a href="https://github.com/" rel="noopener noreferrer external">GitHub</a>, it does delete it but it does not delete the related tag. Typically, if you go back to your repo with your browser, it is still displayed.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*iRWNQQhtlR3gWy65f0qi0g.png" alt="" / loading="lazy"></p>
<p>If you would like to remove such tags, you can do so with the help of a Git push and the option<code>--delete</code> followed by the name of the tag to remove.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">git</span><span style="color:#F1FA8C"> push</span><span style="color:#BD93F9"> --delete</span><span style="color:#F1FA8C"> origin</span><span style="color:#F1FA8C"> v0.0.1</span></span></code></pre><hr />
<h3>Summary</h3>
<p>Hopefully this time I will remember these command lines but if not, at least I will know where to find them 😁.</p>
<p>Stay home, stay safe!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Angular And Web Workers]]></title>
          <description><![CDATA[How to integrate Web Workers to your Angular applications]]></description>
          <link>https://daviddalbusco.com/blog/angular-and-web-workers/</link>
          <pubDate>Wed, 15 Apr 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*0EDeEWFc3O8KxelyJArjjA.png" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@darya_tryfanava?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Darya Tryfanava</a> on <a href="https://unsplash.com/s/photos/free?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I share <a href="https://daviddalbusco.com/blog/how-to-call-the-service-worker-from-the-web-app-context" rel="noopener noreferrer external">one trick a day</a> until the original scheduled date of the end of the COVID-19 quarantine in Switzerland, April 19th 2020. <strong>Four</strong> days left until this first milestone. Hopefully better days are ahead.</p>
<hr />
<p>It has been a long time since the last time <a href="https://angular.io/" rel="noopener noreferrer external">Angular</a> did not make me say out at loud “Wow, that’s pretty neat”, but today was the day again!</p>
<p>Together with my client’s colleagues we had a new requirement which had to do with <a href="https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API" rel="noopener noreferrer external">IndexedDB</a>. For such purpose we notably had to clear the data. As many entries can have been stored, such process can take a while and it was important to not block the UI and the user interaction.</p>
<p>That’s why we developed our feature using <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers" rel="noopener noreferrer external">Web Workers</a> and why I am sharing this new blog post.</p>
<hr />
<h3>Adding A Web Worker</h3>
<p>The Angular team made an outstanding job. Their CLI integration works seamlessly and the <a href="https://angular.io/guide/web-worker" rel="noopener noreferrer external">documentation</a> is straight forward.</p>
<p>To add a Web Worker, we run the command <code>ng generate web-worker</code> followed by the target location, most commonly our <code>app</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">ng</span><span style="color:#F1FA8C"> generate</span><span style="color:#F1FA8C"> web-worker</span><span style="color:#F1FA8C"> app</span></span></code></pre><p>The command will take care of adding a new <a href="https://www.typescriptlang.org/" rel="noopener noreferrer external">TypeScript</a> compiler configuration for our worker but will also generate a sample and its usage within the app.</p>
<p>The sample will find place in <code>./src/app/app.worker.ts</code> . It contains the TypeScript reference and register a listener which can be called to start its work in the worker thread.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">/// &#x3C;</span><span style="color:#FF79C6">reference</span><span style="color:#50FA7B;font-style:italic"> lib</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">webworker</span><span style="color:#E9F284">"</span><span style="color:#6272A4"> /></span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">addEventListener</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">message</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, ({ </span><span style="color:#FFB86C;font-style:italic">data</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> response </span><span style="color:#FF79C6">=</span><span style="color:#F1FA8C"> `worker response to </span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">data</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#50FA7B">    postMessage</span><span style="color:#F8F8F2">(response);</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><p>Its usage will be added to <code>./src/app/app.component.ts</code> . It tests if workers are supported and if yes, build a new object and call the worker respectively instructs it to start its job.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">typeof</span><span style="color:#F8F8F2"> Worker </span><span style="color:#FF79C6">!==</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">undefined</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#6272A4">    // Create a new</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> worker </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Worker</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">./app.worker</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, { type</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">module</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> });</span></span>
<span class="line"><span style="color:#F8F8F2">    worker.</span><span style="color:#50FA7B">onmessage</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">data</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`page got message: </span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">data</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"><span style="color:#F8F8F2">    worker.</span><span style="color:#50FA7B">postMessage</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">hello</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">} </span><span style="color:#FF79C6">else</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#6272A4">    // Web Workers are not supported in this environment.</span></span>
<span class="line"><span style="color:#6272A4">    // You should add a fallback so that your program still executes correctly.</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>Refactor</h3>
<p>In order to use this worker, there is a good chance that we might want to refactor it. I personally like to group my workers in a subfolder <code>./src/app/workers/</code> . I do not know if it is a best practice or not, but a bit like the services, I think it is cool.</p>
<p>Moreover, we may have more than workers in our app. That’s why I also suggest to rename it, for example, let’s call it <code>hello.worker.ts</code> .</p>
<p>In the same way, we might want to call the worker from a <code>service</code> and not from <code>app.component.ts</code> .</p>
<p>Note that in the following example I also rename the worker and modify the relative path to point to the correct location.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Injectable } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Injectable</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    providedIn</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">root</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> HelloService</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    async</span><span style="color:#50FA7B"> sayHello</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">typeof</span><span style="color:#F8F8F2"> Worker </span><span style="color:#FF79C6">!==</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">undefined</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">            const</span><span style="color:#F8F8F2"> worker </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Worker</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">../workers/hello.worker</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, { type</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">module</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">            worker.</span><span style="color:#50FA7B">onmessage</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">data</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">                console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`page got message: </span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">data</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">            };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">            worker.</span><span style="color:#50FA7B">postMessage</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">hello</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Finally, in order to be able to run a test, I call my service from the main page of my application.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Component, OnInit} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {HelloService} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./hello.service</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  selector</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-home</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  templateUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">home.page.html</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  styleUrls</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">home.page.scss</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> HomePage</span><span style="color:#FF79C6"> implements</span><span style="color:#8BE9FD;font-style:italic"> OnInit</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  constructor</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">private</span><span style="color:#FFB86C;font-style:italic"> helloService</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HelloService</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  async</span><span style="color:#50FA7B"> ngOnInit</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.helloService.</span><span style="color:#50FA7B">sayHello</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>All set, we can try to run a test. If everything goes according plan, you should be able to discover a message in the console which follow the exchange between the app and the worker.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*YJ9O5OKJO2ZWHSYbVOrmww.png" alt="" / loading="lazy"></p>
<hr />
<h3>Simulate A Blocked User Interface</h3>
<p>We might like now to test that effectively our worker is performing a job that is not blocking the UI.</p>
<p>I displayed such a test in a previous article about <a href="https://daviddalbusco.com/blog/react-and-web-workers" rel="noopener noreferrer external">React and Web Worker</a>, that’s why we kind of follow the same idea here too. We create two buttons, once which increment “Tomato” using the JavaScript thread and ultimately one which increment “Apple” using a worker thread. But first, let’s do all the work in the JavaScript thread.</p>
<p>In our main template we add these two buttons and link these with their related functions. We also display two labels to show their current values.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">ion-content</span><span style="color:#50FA7B;font-style:italic"> [fullscreen]</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">true</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">ion-label</span><span style="color:#F8F8F2">> Tomato: {{countTomato}} | Apple: {{countApple}} &#x3C;/</span><span style="color:#FF79C6">ion-label</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> className</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ion-padding-top</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">ion-button</span><span style="color:#50FA7B;font-style:italic"> (click)</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">incTomato()</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> color</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">primary</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Tomato&#x3C;/</span><span style="color:#FF79C6">ion-button</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">ion-button</span><span style="color:#50FA7B;font-style:italic"> (click)</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">incApple()</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> color</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">secondary</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Apple&#x3C;/</span><span style="color:#FF79C6">ion-button</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">ion-content</span><span style="color:#F8F8F2">></span></span></code></pre><p>We also implement these states and functions in our main component. Moreover we are adding explicitly a custom delay in our function <code>incApple()</code> in order to simulate a blocking UI interactions.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Component, OnInit} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {HelloService} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">../services/hello.service</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  selector</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-home</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  templateUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">home.page.html</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  styleUrls</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">home.page.scss</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> HomePage</span><span style="color:#FF79C6"> implements</span><span style="color:#8BE9FD;font-style:italic"> OnInit</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#F8F8F2"> countTomato </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#F8F8F2"> countApple </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  constructor</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">private</span><span style="color:#FFB86C;font-style:italic"> helloService</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HelloService</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  async</span><span style="color:#50FA7B"> ngOnInit</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.helloService.</span><span style="color:#50FA7B">sayHello</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  incTomato</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.countTomato</span><span style="color:#FF79C6">++</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  incApple</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> start </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> Date.</span><span style="color:#50FA7B">now</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">    while</span><span style="color:#F8F8F2"> (Date.</span><span style="color:#50FA7B">now</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2"> start </span><span style="color:#FF79C6">+</span><span style="color:#BD93F9"> 5000</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.countApple</span><span style="color:#FF79C6">++</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>If you would test the above in your browser you would effectively notice that as long the “Apple” counter is not resolved, the GUI will not be rendered again and therefor will not been updated.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*OTDF80thurZxQLMwbHtvTA.gif" alt="" / loading="lazy"></p>
<hr />
<h3>Defer Work With Web Workers</h3>
<p>Let’s now try to solve the situation by deferring this custom made delay to our worker thread.</p>
<hr />
<h4>Web Workers</h4>
<p>We move our blocker code to our <code>hello.worker</code> and we also modify it in order to use the <code>data</code> as input for the current counter value.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">/// &#x3C;</span><span style="color:#FF79C6">reference</span><span style="color:#50FA7B;font-style:italic"> lib</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">webworker</span><span style="color:#E9F284">"</span><span style="color:#6272A4"> /></span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">addEventListener</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">message</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, ({ </span><span style="color:#FFB86C;font-style:italic">data</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> start </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> Date.</span><span style="color:#50FA7B">now</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">    while</span><span style="color:#F8F8F2"> (Date.</span><span style="color:#50FA7B">now</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2"> start </span><span style="color:#FF79C6">+</span><span style="color:#BD93F9"> 5000</span><span style="color:#F8F8F2">) {}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    postMessage</span><span style="color:#F8F8F2">(data </span><span style="color:#FF79C6">+</span><span style="color:#BD93F9"> 1</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><hr />
<h4>Services</h4>
<p>To pass data between services and components you can of course either use <a href="https://rxjs-dev.firebaseapp.com/" rel="noopener noreferrer external">RxJS</a> or any other global store solution but for simplicity reason I have use a callback to pass by the result from the web worker to our component state.</p>
<p>What it does is creating the worker object and registering a listener <code>onmessage</code> which listen to the result of the web worker and call our callback with it. Finally it calls the worker to start the job with <code>postMessage</code> and provide the current counter as parameter.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Injectable } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Injectable</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  providedIn</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">root</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> HelloService</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  async</span><span style="color:#50FA7B"> countApple</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">counter</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#50FA7B">                   updateCounter</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#8BE9FD;font-style:italic"> void</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">typeof</span><span style="color:#F8F8F2"> Worker </span><span style="color:#FF79C6">!==</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">undefined</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">      const</span><span style="color:#F8F8F2"> worker </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#FF79C6;font-weight:bold">          new</span><span style="color:#50FA7B"> Worker</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">../workers/hello.worker</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, { type</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">module</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2"> });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">      worker.</span><span style="color:#50FA7B">onmessage</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">data</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">        updateCounter</span><span style="color:#F8F8F2">(data);</span></span>
<span class="line"><span style="color:#F8F8F2">      };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">      worker.</span><span style="color:#50FA7B">postMessage</span><span style="color:#F8F8F2">(counter);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h4>Component</h4>
<p>Our service has changed, that’s why we have to reflect the modification in the component. On the template side nothing needs to be modified but on the code side we have to use the new exposed function <code>countApple</code> from the service and have to provide both current “Apple” counter value and a callback to update this<br />
state once the worker will have finish its computation.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Component} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {HelloService} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">../services/hello.service</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  selector</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-home</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  templateUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">home.page.html</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  styleUrls</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">home.page.scss</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> HomePage</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#F8F8F2"> countTomato </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#F8F8F2"> countApple </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  constructor</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">private</span><span style="color:#FFB86C;font-style:italic"> helloService</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HelloService</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  incTomato</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.countTomato</span><span style="color:#FF79C6">++</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  async</span><span style="color:#50FA7B"> incApple</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.helloService.</span><span style="color:#50FA7B">countApple</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.countApple,</span></span>
<span class="line"><span style="color:#F8F8F2">               (</span><span style="color:#FFB86C;font-style:italic">value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.countApple </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> value);</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>If you would run the example in your browser you should be able to notice that our interaction and UI aren’t blocked anymore, tada 🎉.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*IEjanj7fsFvpvcXGbFSzsg.gif" alt="" / loading="lazy"></p>
<hr />
<h3>Cherry On Top</h3>
<p>You know what’s really, but really, cool with this Angular Web Worker integration? You can use your dependencies in your worker too!</p>
<p>For example, if your application is using <a href="https://github.com/jakearchibald/idb-keyval" rel="noopener noreferrer external">idb-keyval</a>, you can import it and use it in your worker out of the box, no configuration needed.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">/// &#x3C;</span><span style="color:#FF79C6">reference</span><span style="color:#50FA7B;font-style:italic"> lib</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">webworker</span><span style="color:#E9F284">"</span><span style="color:#6272A4"> /></span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { set } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">idb-keyval</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">addEventListener</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">message</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">data</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#50FA7B"> set</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">hello</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">world</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    postMessage</span><span style="color:#F8F8F2">(data);</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><hr />
<h3>Summary</h3>
<p>I like Web Workers 😸</p>
<p>Stay home, stay safe!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Follow-up: Web Push Notifications And PWA In 2020]]></title>
          <description><![CDATA[A follow-up to my one year old tutorial Web Push Notifications In Progressive Web Apps with Ionic And Angular]]></description>
          <link>https://daviddalbusco.com/blog/follow-up-web-push-notifications-and-pwa-in-2020/</link>
          <pubDate>Tue, 14 Apr 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*W6Eot89ZwJa994WQum55Sg.png" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@soymeraki?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Javier Allegue Barros</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I share <a href="https://medium.com/@david.dalbusco/one-trick-a-day-d-34-469a0336a07e" rel="noopener noreferrer external">one trick a day</a> until the original scheduled date of the end of the COVID-19 quarantine in Switzerland, April 19th 2020. <strong>Five</strong> days left until this first milestone. Hopefully better days are ahead.</p>
<hr />
<p>If you follow me on <a href="https://twitter.com/daviddalbusco" rel="noopener noreferrer external">Twitter</a>, you may have read that an application I developed and recently submitted to the stores have been rejected by both Apple and Google because it was not aligned, according them, with their restrictive policy regarding the current COVID-19 pandemic.</p>
<p>I am not writing these lines to share my opinion on these companies, but to share a follow-up to my one year old tutorial: <a href="https://medium.com/@david.dalbusco/add-web-push-notifications-to-your-ionic-pwa-358f6ec53c6f" rel="noopener noreferrer external">Web Push Notifications in Progressive Web Apps</a>.</p>
<p>Indeed one core concept of the app which was rejected relies on Push Notifications. As it is developed with <a href="https://ionicframework.com" rel="noopener noreferrer external">Ionic</a> and <a href="https://angular.io" rel="noopener noreferrer external">Angular</a>, we are able to unleash a Progressive Web Apps, but is such feature yet well supported?</p>
<hr />
<h3>Introduction</h3>
<p>I am writing this article <strong>Tuesday 14th April 2020</strong>, that’s why it reflects the status of that specific date. If you would read this in the future and notice improvements or changes, ping me!</p>
<p>This afternoon I ran my tests on my Android phone, a OnePlus 6 running Android v10 and on my iPhone 6s running iOS 13.</p>
<hr />
<h3>Android</h3>
<p>It works like a charm, period. I tested Web Push Notifications with my phone in idle mode, awake and with the application open. In all cases, I received the notifications. Great work Google 👍.</p>
<hr />
<h3>iOS</h3>
<p>Web Push Notifications are still <strong>not supported</strong> on iOS. The status didn’t change since I published my tutorial in February 2019. As you can notice with the help of <a href="https://caniuse.com/#search=notification" rel="noopener noreferrer external">Caniuse</a>, the Notifications API is not yet implemented by iOS Safari.</p>
<hr />
<h3>Setup</h3>
<p>The <a href="https://firebase.google.com/docs/cloud-messaging" rel="noopener noreferrer external">Firebase Cloud Messaging</a> set up I displayed in my previous article still remains valid. Of course maybe some screenshots have changed or have been actualized, but the idea remains the same. Moreover, I have set up the tokens for my application in the exact same way and everything went fine.</p>
<p>An interesting thing to notice though, it the <a href="https://medium.com/@galilo7g/good-tutorial-just-mention-the-need-to-specify-the-firebase-version-in-the-service-worker-that-e90d3d8a2231" rel="noopener noreferrer external">comment</a> from <a href="https://medium.com/@galilo7g" rel="noopener noreferrer external">Galilo Galilo</a>. According his/her experience, the Firebase dependencies used in the service worker had to be set as the exact same version number as the one used in <code>package.json</code> . I did not had this problem but it is something which might be worth to keep in mind.</p>
<hr />
<h3>Implementation</h3>
<p>To the exception of the following deprecation, which can or not be improved, the <a href="https://medium.com/@david.dalbusco/deeplinking-in-ionic-apps-with-branch-io-ba1a1c4ed227" rel="noopener noreferrer external">implementation</a> displayed in my previous tutorial remains also valid. It is the one I have implemented in our application and therefor the one I successfully tested today on my Android phone.</p>
<p>That being said, I think that there might be an easier way, specially if you are using <a href="https://github.com/angular/angularfire" rel="noopener noreferrer external">AngularFire</a>, to implement Web Push Notifications in a Progressive Web Apps. I did not check it out but before following my tutorial it maybe deserves a quick research, just in case you would be able to spare some time 😉.</p>
<hr />
<h4>Deprecation</h4>
<p>Not a big deal but while having a look at the code I noticed that <code>await messaging.requestPermission();</code> was marked as deprecated. It can be updated as following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">if</span><span style="color:#F8F8F2"> (Notification.permission </span><span style="color:#FF79C6">!==</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">denied</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#F8F8F2"> Notification.</span><span style="color:#50FA7B">requestPermission</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h4>Altogether</h4>
<p>Altogether, my enhanced Angular service which takes care of registering the Web Push Notifications and requesting the permissions.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Injectable} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {firebase} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@firebase/app</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@firebase/messaging</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {environment} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">../../../environments/environment</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Injectable</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    providedIn</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">root</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> FirebaseNotificationsPwaService</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    async</span><span style="color:#50FA7B"> init</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#F8F8F2">        navigator.serviceWorker.ready.</span><span style="color:#50FA7B">then</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">registration</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">            if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">firebase.messaging.</span><span style="color:#50FA7B">isSupported</span><span style="color:#F8F8F2">()) {</span></span>
<span class="line"><span style="color:#FF79C6">                return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">            const</span><span style="color:#F8F8F2"> messaging </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> firebase.</span><span style="color:#50FA7B">messaging</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">            messaging.</span><span style="color:#50FA7B">useServiceWorker</span><span style="color:#F8F8F2">(registration);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">             messaging</span></span>
<span class="line"><span style="color:#F8F8F2">                 .</span><span style="color:#50FA7B">usePublicVapidKey</span><span style="color:#F8F8F2">(environment.firebase.vapidKey);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">            messaging.</span><span style="color:#50FA7B">onMessage</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">payload</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#6272A4">                // If we want to display</span></span>
<span class="line"><span style="color:#6272A4">                // a msg when the app is in foreground</span></span>
<span class="line"><span style="color:#F8F8F2">                console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(payload);</span></span>
<span class="line"><span style="color:#F8F8F2">            });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">            // Handle token refresh</span></span>
<span class="line"><span style="color:#F8F8F2">            messaging.</span><span style="color:#50FA7B">onTokenRefresh</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">                messaging.</span><span style="color:#50FA7B">getToken</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">then</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#F8F8F2">                    (</span><span style="color:#FFB86C;font-style:italic">refreshedToken</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">                    console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(refreshedToken);</span></span>
<span class="line"><span style="color:#F8F8F2">                }).</span><span style="color:#50FA7B">catch</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">err</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">                    console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(err);</span></span>
<span class="line"><span style="color:#F8F8F2">                });</span></span>
<span class="line"><span style="color:#F8F8F2">            });</span></span>
<span class="line"><span style="color:#F8F8F2">        }, (</span><span style="color:#FFB86C;font-style:italic">err</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">            console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(err);</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    async</span><span style="color:#50FA7B"> requestPermission</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">Notification) {</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">firebase.messaging.</span><span style="color:#50FA7B">isSupported</span><span style="color:#F8F8F2">()) {</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">            const</span><span style="color:#F8F8F2"> messaging </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> firebase.</span><span style="color:#50FA7B">messaging</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">            if</span><span style="color:#F8F8F2"> (Notification.permission </span><span style="color:#FF79C6">!==</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">denied</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">                await</span><span style="color:#F8F8F2"> Notification.</span><span style="color:#50FA7B">requestPermission</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">            const</span><span style="color:#F8F8F2"> token</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> messaging.</span><span style="color:#50FA7B">getToken</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">            // User token</span></span>
<span class="line"><span style="color:#F8F8F2">            console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(token);</span></span>
<span class="line"><span style="color:#F8F8F2">        } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#F8F8F2">            console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(err);</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>Summary</h3>
<p>Hopefully one day we will be able to send Web Push Notifications on iOS devices too 🤞.</p>
<p>Stay home, stay safe!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Deeplinking in Ionic Apps With Branch.io]]></title>
          <description><![CDATA[How to set up deep links for your Ionic Apps with Branch.io and intercept parameters]]></description>
          <link>https://daviddalbusco.com/blog/deeplinking-in-ionic-apps-with-branch-io/</link>
          <pubDate>Mon, 13 Apr 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*AbG39baPvtxSgOoSjWgHqg.png" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@soymeraki?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Javier Allegue Barros</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I share <a href="https://daviddalbusco.com/blog/how-to-call-the-service-worker-from-the-web-app-context" rel="noopener noreferrer external">one trick a day</a> until the original scheduled date of the end of the COVID-19 quarantine in Switzerland, April 19th 2020. <strong>Six</strong> days left until this first milestone. Hopefully better days are ahead.</p>
<hr />
<p>Last Friday I had to quickly implement the deeplinking support for an application that we were finalizing. Its goals was, and still is, to help isolated people to feel less alone to find out easily for help within their relatives, something really useful in normal times, specially for older people we think, but maybe even more when there is a crisis.</p>
<p>Turns out the Apple rejected it on Saturday as they think that our project is only COVID-19 focused and only accept related projects from certified organizations 😢. If interested, CNBC published an article about the <a href="https://www.cnbc.com/2020/03/05/apple-rejects-coronavirus-apps-that-arent-from-health-organizations.html" rel="noopener noreferrer external">subject</a>.</p>
<p>Anyway, not here to share my opinion on companies which became more powerful than states and their people but here to share technical tips and tricks 😉.</p>
<p>That’s why here is how I implemented quickly such a concept including how to catch linked parameters.</p>
<hr />
<h3>What’s Deeplinking?</h3>
<p>To me deeplinking solve miscellaneous problematics linked to mobile applications:</p>
<ol>
<li><p>An app can be available in the App Store, Google Play, as a Progressive Web Apps and probably even in other stores. For all of them, it will be accessible with the help of a different link or URI, therefor, it makes your communication a bit difficult as you have to communicate multiple links (“If you are using Android, click here. If you are using iOS, click here. Etc.). Thanks to deep links it is possible to provide one single URL which link the user to the appropriate resource, appropriate target.</p></li>
<li><p>Users may have or not already installed you application. That’s why, when you provide them a link, you might want to link them either straight to it, if already installed, or automatically to the store if they don’t have it yet installed.</p></li>
<li><p>Finally, you might have to provide parameters to your application. What would happen if a user click a link, does not have the application, goes to the store, install the application and then start it? The parameters would be lost. That is also were deep linking can help to maintain and provide the parameters until the user effectively start the application.</p></li>
</ol>
<hr />
<h3>Deeplinking Vs Custom URI Scheme</h3>
<p>Deeplinking should not be confused with a custom URI scheme.</p>
<p>A custom URI scheme, for example <code>myapp://</code> , is a scheme which can be handle on the mobile devices to call your application <strong>but</strong> this works only once the application has been installed. Before that point, the devices has no idea of such scheme.</p>
<hr />
<h3>Branch.io</h3>
<p>I generally don’t write about none open source solutions but so far, I did not found better solution than <a href="https://branch.io" rel="noopener noreferrer external">Branch.io</a> to setup deep links.</p>
<p>There is a <a href="https://github.com/ionic-team/ionic-plugin-deeplinks" rel="noopener noreferrer external">Cordova Ionic plugin</a> to handle such links which is maintained by the community. When <a href="https://ionicframework.com" rel="noopener noreferrer external">Ionic</a> introduced it some years ago I used it at first but finally went for the Branch solution but to be honest I don’t remember exactly why, probably a specific case.</p>
<p>Branch has a starter free plan.</p>
<hr />
<h3>Setup</h3>
<p>Branch provide a comprehensive <a href="https://help.branch.io/developers-hub/docs/cordova-phonegap-ionic" rel="noopener noreferrer external">documentation</a> about how to configure their services and even their platform, once you are registered, is pretty straight forward to be used.</p>
<p>Only important things worth to notice: your application <strong>has to be available in store</strong> before being able to properly setup deep links. When you configure it, you will have to search it and link it with your account, therefor it has to be public and available first, otherwise you won’t be able to finish the configuration.</p>
<p>But, worth to notice, that you don’t have to wait for publishing to already implement and test it.</p>
<hr />
<h3>That’s It</h3>
<p>That’s already it! If you only goal is to provide deep links to our, current and potential, users which point either to your app if installed or to the store with a single link, you are done.</p>
<p>No need to install a plugin, regardless if you are using <a href="https://cordova.apache.org" rel="noopener noreferrer external">Cordova</a> or <a href="https://capacitor.ionicframework.com" rel="noopener noreferrer external">Capacitor</a>.</p>
<p>For example, check out the source code of my app Tie Tracker on <a href="https://github.com/peterpeterparker/tietracker" rel="noopener noreferrer external">GitHub</a>. As you notice, there isn’t any references to Branch but even though, I am able to provide a single link <a href="https://tietracker.app.link/" rel="noopener noreferrer external">https://tietracker.app.link/</a> which will guide you either to the installed app, the store or if none of these to the PWA.</p>
<hr />
<h3>Intercept Parameters</h3>
<p>That being setup, you may be interested to intercept parameters across the all process by keeping in mind of course that tracking is bad and only anonymous usage is acceptable.</p>
<hr />
<h4>Installation</h4>
<p>The related Cordova plugin finds place in <a href="https://www.npmjs.com/package/branch-cordova-sdk" rel="noopener noreferrer external">npm</a> and can be use as following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">ionic</span><span style="color:#F1FA8C"> cordova</span><span style="color:#F1FA8C"> plugin</span><span style="color:#F1FA8C"> add</span><span style="color:#F1FA8C"> branch-cordova-sdk</span></span></code></pre><hr />
<h4>Configuration</h4>
<p>One installed, you will have to configure it for your platforms. In your <code>config.xml</code> a related entry will have to be added.</p>
<p>All Branch’s information are going to be provided by them when you set up your application and the iOS team release identifier finds place in your Apple “appstoreconnect” dashboard.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">branch-config</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">branch-key</span><span style="color:#50FA7B;font-style:italic"> value</span><span style="color:#F8F8F2">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">key_live_2ad987a7d8798d7a7da87ad8747</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">uri-scheme</span><span style="color:#50FA7B;font-style:italic"> value</span><span style="color:#F8F8F2">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">myapp</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">link-domain</span><span style="color:#50FA7B;font-style:italic"> value</span><span style="color:#F8F8F2">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">myapp.app.link</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">ios-team-release</span><span style="color:#50FA7B;font-style:italic"> value</span><span style="color:#F8F8F2">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">BE88ABJS2W</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">branch-config</span><span style="color:#F8F8F2">></span></span></code></pre><hr />
<h3>Implementation</h3>
<p><a href="https://ionicframework.com/docs/native/branch-io" rel="noopener noreferrer external">Ionic Native</a> provides support for Branch but I never used it so far, that’s why I will not use it in the following example. I also only implemented it in <a href="https://angular.io/" rel="noopener noreferrer external">Angular</a> apps with Cordova, that’s why I’m using here such technologies.</p>
<p>In our main component <code>app.component.ts</code> we first declare a new interface which represent the parameter we are interested to intercept. In this example I named it <code>$myparam</code> . Worth to notice that, out of the box, Branch fill forward parameters prefixed with <strong>$.</strong></p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">interface</span><span style="color:#8BE9FD;font-style:italic"> DeeplinkMatch</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    $myparam</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Once our main component is initialized, we add a listener on the platform in order to initialize the interception only once it is mounted.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">ngAfterViewInit</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.platform.</span><span style="color:#50FA7B">ready</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">then</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">initDeepLinking</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>According the device of the user, he/she may start the application either as a mobile app or as a web application. These are two different ways of querying the parameters and that’s why we are handling the differently.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">private async </span><span style="color:#50FA7B">initDeepLinking</span><span style="color:#F8F8F2">(): </span><span style="color:#8BE9FD;font-style:italic">Promise</span><span style="color:#FF79C6">&#x3C;void></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    if</span><span style="color:#F8F8F2"> (this.platform.is(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">cordova</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">)) {</span></span>
<span class="line"><span style="color:#FF79C6">        await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">initDeepLinkingBranchio</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    } else {</span></span>
<span class="line"><span style="color:#FF79C6">        await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">initDeepLinkingWeb</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>When it comes to the web, we can search for the parameters using the <code>platform</code>. It they can be provided and prefixed in different ways, sure thing, I like to test all possibilities.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">private async </span><span style="color:#50FA7B">initDeepLinkingWeb</span><span style="color:#F8F8F2">(): </span><span style="color:#8BE9FD;font-style:italic">Promise</span><span style="color:#FF79C6">&#x3C;void></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    const myparam</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> string </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">                   this</span><span style="color:#F8F8F2">.platform.</span><span style="color:#50FA7B">getQueryParam</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">$myparam</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">||</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">                   this</span><span style="color:#F8F8F2">.platform.</span><span style="color:#50FA7B">getQueryParam</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">myparam</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">||</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">                   this</span><span style="color:#F8F8F2">.platform.</span><span style="color:#50FA7B">getQueryParam</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">%24myparam</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Parameter</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, myparam);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Finally, we can handle the mobile app parameter provided by Branch.</p>
<p>Important to notice is the fact that the parameter is provided <strong>asynchronously</strong>. That’s why you can’t amend that it is present at startup but have to think that it may be provided afterwards with delay.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">private async </span><span style="color:#50FA7B">initDeepLinkingBranchio</span><span style="color:#F8F8F2">(): </span><span style="color:#8BE9FD;font-style:italic">Promise</span><span style="color:#FF79C6">&#x3C;void></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    try {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> branchIo </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> window[</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Branch</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">];</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (branchIo) {</span></span>
<span class="line"><span style="color:#FF79C6">            const</span><span style="color:#F8F8F2"> data</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> DeeplinkMatch</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#FF79C6">                        await</span><span style="color:#F8F8F2"> branchIo.</span><span style="color:#50FA7B">initSession</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">            if</span><span style="color:#F8F8F2"> (data.$myparam </span><span style="color:#FF79C6">!==</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">                console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Parameter</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, data.$myparam);</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    } catch (err) {</span></span>
<span class="line"><span style="color:#F8F8F2">        console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">err</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>And voilà 🥳, we are able to handle deep links with parameters. If for example we would provide an URL such as <a href="https://myapp.app.link/?$myparam=yolo" rel="noopener noreferrer external">https://myapp.app.link/?$myparam=yolo</a> to our users, we would be able to intercept “yolo” 😁.</p>
<hr />
<h4>Altogether</h4>
<p>In case you would need it, here is the above code in a single piece:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {AfterViewInit, Component} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Platform} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@ionic/angular</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {SplashScreen} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@ionic-native/splash-screen/ngx</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {StatusBar} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@ionic-native/status-bar/ngx</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">interface</span><span style="color:#8BE9FD;font-style:italic"> DeeplinkMatch</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    $myparam</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    selector</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-root</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    templateUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app.component.html</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    styleUrls</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">app.component.scss</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">]</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> AppComponent</span><span style="color:#FF79C6"> implements</span><span style="color:#8BE9FD;font-style:italic"> AfterViewInit</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    constructor</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#FF79C6">        private</span><span style="color:#FFB86C;font-style:italic"> platform</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Platform</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#FF79C6">        private</span><span style="color:#FFB86C;font-style:italic"> splashScreen</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> SplashScreen</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#FF79C6">        private</span><span style="color:#FFB86C;font-style:italic"> statusBar</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> StatusBar</span></span>
<span class="line"><span style="color:#F8F8F2">    ) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">initializeApp</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    initializeApp</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        this</span><span style="color:#F8F8F2">.platform.</span><span style="color:#50FA7B">ready</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">then</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">            this</span><span style="color:#F8F8F2">.statusBar.</span><span style="color:#50FA7B">styleDefault</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">            this</span><span style="color:#F8F8F2">.splashScreen.</span><span style="color:#50FA7B">hide</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    ngAfterViewInit</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        this</span><span style="color:#F8F8F2">.platform.</span><span style="color:#50FA7B">ready</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">then</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">            await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">initDeepLinking</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    private</span><span style="color:#FF79C6"> async</span><span style="color:#50FA7B"> initDeepLinking</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">void</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.platform.</span><span style="color:#50FA7B">is</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">cordova</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">)) {</span></span>
<span class="line"><span style="color:#FF79C6">            await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">initDeepLinkingBranchio</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">        } </span><span style="color:#FF79C6">else</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">            await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">initDeepLinkingWeb</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    private</span><span style="color:#FF79C6"> async</span><span style="color:#50FA7B"> initDeepLinkingWeb</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">void</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> myparam</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">                       this</span><span style="color:#F8F8F2">.platform.</span><span style="color:#50FA7B">getQueryParam</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">$myparam</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">||</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">                       this</span><span style="color:#F8F8F2">.platform.</span><span style="color:#50FA7B">getQueryParam</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">myparam</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">||</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">                       this</span><span style="color:#F8F8F2">.platform.</span><span style="color:#50FA7B">getQueryParam</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">%24myparam</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">        console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Parameter</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, myparam);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    private</span><span style="color:#FF79C6"> async</span><span style="color:#50FA7B"> initDeepLinkingBranchio</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">void</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">        try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">            const</span><span style="color:#F8F8F2"> branchIo </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> window[</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Branch</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">            if</span><span style="color:#F8F8F2"> (branchIo) {</span></span>
<span class="line"><span style="color:#FF79C6">                const</span><span style="color:#F8F8F2"> data</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> DeeplinkMatch</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#FF79C6">                            await</span><span style="color:#F8F8F2"> branchIo.</span><span style="color:#50FA7B">initSession</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">                if</span><span style="color:#F8F8F2"> (data.$myparam </span><span style="color:#FF79C6">!==</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">                    console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Parameter</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, data.$myparam);</span></span>
<span class="line"><span style="color:#F8F8F2">                }</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"><span style="color:#F8F8F2">        } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#F8F8F2">            console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(err);</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>Summary</h3>
<p>It works out. To be honest with you, not the funniest part of the job, not that it is complicated or anything to set up or to be used, just, not really fun.</p>
<p>Stay home, stay safe!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[JavaScript Useful Functions]]></title>
          <description><![CDATA[A couple of useful JavaScript functions such as unifying events, debouncing or detecting mobile browsers]]></description>
          <link>https://daviddalbusco.com/blog/javascript-useful-functions/</link>
          <pubDate>Sun, 12 Apr 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*qvaNuuGMVDesVbjYY_fdoQ.png" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@sammoqadam?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Sam Moqadam</a> on <a href="https://unsplash.com/s/photos/free?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I share <a href="https://daviddalbusco.com/blog/how-to-call-the-service-worker-from-the-web-app-context" rel="noopener noreferrer external">one trick a day</a> until the original scheduled date of the end of the COVID-19 quarantine in Switzerland, April 19th 2020. <strong>Seven</strong> days left until this first milestone. Hopefully better days are ahead.</p>
<hr />
<p>We are using a couple of JavaScript functions across the multiple applications and Web Components of <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>.</p>
<p>Maybe these can be useful for your projects too?</p>
<hr />
<h3>Unifying Mouse And Touch Events</h3>
<p>If you are implementing functions which have to do with user interactions through mouse or touch devices, there is a good change that their outcome are the same.</p>
<p>For example, presenters can draw over their presentations using our <a href="https://deckdeckgo.app" rel="noopener noreferrer external">remote control</a>. For such purpose we use canvas and are attaching events such as:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> canvas </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> document.</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">canvas</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">canvas.</span><span style="color:#50FA7B">addEventListener</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">mousedown</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.startEvent);</span></span>
<span class="line"><span style="color:#F8F8F2">canvas.</span><span style="color:#50FA7B">addEventListener</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">touchstart</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.startEvent);</span></span></code></pre><p>As you can notice, both <code>mouse</code> and <code>touch</code> events are handled by the same functions, which is good, but unfortunately, can’t work out without a bit of logic if you would like to access to positions information as for example <code>clientX</code> or <code>clientY</code> .</p>
<p>Indeed, <code>touch</code> positioning are not available at the root of the object but rather in an array <code>changedTouches</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">function</span><span style="color:#50FA7B"> startEvent</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">$event</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#6272A4">    // MouseEvent</span></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">($event.clientX);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // TouchEvent</span></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">($event.changedTouches[</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">].clientX);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>That’s why, we are using a function we have called <code>unifyEvents</code> to get the positioning regardless of the devices.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> unifyEvent</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">$event</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> $event.changedTouches </span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2"> $event.changedTouches[</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">] </span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> $event;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Which can be use as the following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">function</span><span style="color:#50FA7B"> startEvent</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">$event</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#6272A4">    // TouchEvent and MouseEvent unified</span></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">unifyEvent</span><span style="color:#F8F8F2">($event).clientX);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>Debouncing</h3>
<p>I covered the topic debouncing with JavaScript in a <a href="https://daviddalbusco.com/blog/debounce-with-vanilla-javascript-or-rxjs" rel="noopener noreferrer external">previous article</a> but if you are looking to add such feature to your application without any dependencies, here’s a function to do so.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> debounce</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">func</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">timeout</span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">  let</span><span style="color:#F8F8F2"> timer;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">...</span><span style="color:#FFB86C;font-style:italic">args</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#50FA7B"> next</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> func</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">...</span><span style="color:#F8F8F2">args);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (timer) {</span></span>
<span class="line"><span style="color:#50FA7B">      clearTimeout</span><span style="color:#F8F8F2">(timer);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    timer </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#50FA7B">      setTimeout</span><span style="color:#F8F8F2">(next, timeout </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> timeout </span><span style="color:#FF79C6">></span><span style="color:#BD93F9"> 0</span><span style="color:#FF79C6"> ?</span><span style="color:#F8F8F2"> timeout </span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 300</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">  };</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>User Agent</h3>
<p>Earlier this year, Google has announced its decision to drop support for the User-Agent string in its Chrome browser (<a href="https://www.infoq.com/news/2020/03/chrome-phasing-user-agent/" rel="noopener noreferrer external">article</a> / <a href="https://github.com/WICG/ua-client-hints" rel="noopener noreferrer external">GitHub</a>).</p>
<p>Therefor the following methods should be used wisely, knowing they will have to be replaced in the future.</p>
<p>That being said, here are a couple of handy functions which help detects information about the type of browser or device.</p>
<hr />
<h4>isMobile</h4>
<p><a href="https://detectmobilebrowsers.com" rel="noopener noreferrer external">Detect Mobile Browsers</a> is providing an open source list of mobile devices which can be use to test our navigator to detect if the user is browsing or using the application on a mobile device or not.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> isMobile</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">window </span><span style="color:#FF79C6">||</span><span style="color:#FF79C6"> !</span><span style="color:#F8F8F2">navigator) {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> a </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#F8F8F2">        navigator.userAgent </span><span style="color:#FF79C6">||</span><span style="color:#F8F8F2"> navigator.vendor </span><span style="color:#FF79C6">||</span></span>
<span class="line"><span style="color:#F8F8F2">        (window </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> any</span><span style="color:#F8F8F2">).opera;</span></span>
<span class="line"><span style="color:#6272A4">  // Regex Source -> http://detectmobilebrowsers.com</span></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#FF5555">/</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">android</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">bb</span><span style="color:#BD93F9">\d</span><span style="color:#FF79C6">+|</span><span style="color:#F1FA8C">meego</span><span style="color:#FFB86C">)</span><span style="color:#BD93F9">.</span><span style="color:#FF79C6">+</span><span style="color:#F1FA8C">mobile</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">avantgo</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">bada\/</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">blackberry</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">blazer</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">compal</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">elaine</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">fennec</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">hiptop</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">iemobile</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">ip</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">hone</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">od</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">ad</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">iris</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">kindle</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">lge </span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">maemo</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">midp</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">mmp</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">mobile</span><span style="color:#BD93F9">.</span><span style="color:#FF79C6">+</span><span style="color:#F1FA8C">firefox</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">netfront</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">opera m</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">ob</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">in</span><span style="color:#FFB86C">)</span><span style="color:#F1FA8C">i</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">palm</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C"> os</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">?|</span><span style="color:#F1FA8C">phone</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">p</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">ixi</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">re</span><span style="color:#FFB86C">)</span><span style="color:#F1FA8C">\/</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">plucker</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">pocket</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">psp</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">series</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">4</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">6</span><span style="color:#FFB86C">)</span><span style="color:#F1FA8C">0</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">symbian</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">treo</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">up\.</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">browser</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">link</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">vodafone</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">wap</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">windows ce</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">xda</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">xiino</span><span style="color:#FF5555">/</span><span style="color:#FF79C6">i</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">test</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#F8F8F2">      a</span></span>
<span class="line"><span style="color:#F8F8F2">    ) </span><span style="color:#FF79C6">||</span></span>
<span class="line"><span style="color:#FF5555">    /</span><span style="color:#F1FA8C">1207</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">6310</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">6590</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">3gso</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">4thp</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">50</span><span style="color:#8BE9FD">[</span><span style="color:#BD93F9">1-6</span><span style="color:#8BE9FD">]</span><span style="color:#F1FA8C">i</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">770s</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">802s</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">a wa</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">abac</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">ac</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">er</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">oo</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">s\-</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">ai</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">ko</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">rn</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">al</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">av</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">ca</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">co</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">amoi</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">an</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">ex</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">ny</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">yw</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">aptu</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">ar</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">ch</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">go</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">as</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">te</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">us</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">attw</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">au</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">di</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">\-m</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">r </span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">s </span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">avan</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">be</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">ck</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">ll</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">nq</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">bi</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">lb</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">rd</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">bl</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">ac</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">az</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">br</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">e</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">v</span><span style="color:#FFB86C">)</span><span style="color:#F1FA8C">w</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">bumb</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">bw\-</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">n</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">u</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">c55\/</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">capi</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">ccwa</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">cdm\-</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">cell</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">chtm</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">cldc</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">cmd\-</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">co</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">mp</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">nd</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">craw</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">da</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">it</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">ll</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">ng</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">dbte</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">dc\-s</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">devi</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">dica</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">dmob</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">do</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">c</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">p</span><span style="color:#FFB86C">)</span><span style="color:#F1FA8C">o</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">ds</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">12</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">\-d</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">el</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">49</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">ai</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">em</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">l2</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">ul</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">er</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">ic</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">k0</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">esl8</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">ez</span><span style="color:#FFB86C">(</span><span style="color:#8BE9FD">[</span><span style="color:#BD93F9">4-7</span><span style="color:#8BE9FD">]</span><span style="color:#F1FA8C">0</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">os</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">wa</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">ze</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">fetc</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">fly</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">\-</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">_</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">g1 u</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">g560</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">gene</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">gf\-5</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">g\-mo</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">go</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">\.w</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">od</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">gr</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">ad</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">un</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">haie</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">hcit</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">hd\-</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">m</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">p</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">t</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">hei\-</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">hi</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">pt</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">ta</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">hp</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C"> i</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">ip</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">hs\-c</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">ht</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">c</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">\-</span><span style="color:#FF79C6">|</span><span style="color:#FF79C6"> |</span><span style="color:#F1FA8C">_</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">a</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">g</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">p</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">s</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">t</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">tp</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">hu</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">aw</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">tc</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">i\-</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">20</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">go</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">ma</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">i230</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">iac</span><span style="color:#FFB86C">(</span><span style="color:#FF79C6"> |</span><span style="color:#F1FA8C">\-</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">\/</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">ibro</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">idea</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">ig01</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">ikom</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">im1k</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">inno</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">ipaq</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">iris</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">ja</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">t</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">v</span><span style="color:#FFB86C">)</span><span style="color:#F1FA8C">a</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">jbro</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">jemu</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">jigs</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">kddi</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">keji</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">kgt</span><span style="color:#FFB86C">(</span><span style="color:#FF79C6"> |</span><span style="color:#F1FA8C">\/</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">klon</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">kpt </span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">kwc\-</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">kyo</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">c</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">k</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">le</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">no</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">xi</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">lg</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C"> g</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">\/</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">k</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">l</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">u</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">50</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">54</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">\-</span><span style="color:#8BE9FD">[</span><span style="color:#BD93F9">a-w</span><span style="color:#8BE9FD">]</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">libw</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">lynx</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">m1\-w</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">m3ga</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">m50\/</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">ma</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">te</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">ui</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">xo</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">mc</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">01</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">21</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">ca</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">m\-cr</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">me</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">rc</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">ri</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">mi</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">o8</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">oa</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">ts</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">mmef</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">mo</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">01</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">02</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">bi</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">de</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">do</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">t</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">\-</span><span style="color:#FF79C6">|</span><span style="color:#FF79C6"> |</span><span style="color:#F1FA8C">o</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">v</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">zz</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">mt</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">50</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">p1</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">v </span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">mwbp</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">mywa</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">n10</span><span style="color:#8BE9FD">[</span><span style="color:#BD93F9">0-2</span><span style="color:#8BE9FD">]</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">n20</span><span style="color:#8BE9FD">[</span><span style="color:#BD93F9">2-3</span><span style="color:#8BE9FD">]</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">n30</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">0</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">2</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">n50</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">0</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">2</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">5</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">n7</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">0</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">0</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">1</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">10</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">ne</span><span style="color:#FFB86C">((</span><span style="color:#F1FA8C">c</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">m</span><span style="color:#FFB86C">)</span><span style="color:#F1FA8C">\-</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">on</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">tf</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">wf</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">wg</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">wt</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">nok</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">6</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">i</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">nzph</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">o2im</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">op</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">ti</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">wv</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">oran</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">owg1</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">p800</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">pan</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">a</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">d</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">t</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">pdxg</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">pg</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">13</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">\-</span><span style="color:#FFB86C">(</span><span style="color:#8BE9FD">[</span><span style="color:#BD93F9">1-8</span><span style="color:#8BE9FD">]</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">c</span><span style="color:#FFB86C">))</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">phil</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">pire</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">pl</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">ay</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">uc</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">pn\-2</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">po</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">ck</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">rt</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">se</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">prox</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">psio</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">pt\-g</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">qa\-a</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">qc</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">07</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">12</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">21</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">32</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">60</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">\-</span><span style="color:#8BE9FD">[</span><span style="color:#BD93F9">2-7</span><span style="color:#8BE9FD">]</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">i\-</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">qtek</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">r380</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">r600</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">raks</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">rim9</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">ro</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">ve</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">zo</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">s55\/</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">sa</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">ge</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">ma</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">mm</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">ms</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">ny</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">va</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">sc</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">01</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">h\-</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">oo</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">p\-</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">sdk\/</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">se</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">c</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">\-</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">0</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">1</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">47</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">mc</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">nd</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">ri</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">sgh\-</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">shar</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">sie</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">\-</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">m</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">sk\-0</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">sl</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">45</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">id</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">sm</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">al</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">ar</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">b3</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">it</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">t5</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">so</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">ft</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">ny</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">sp</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">01</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">h\-</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">v\-</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">v </span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">sy</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">01</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">mb</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">t2</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">18</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">50</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">t6</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">00</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">10</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">18</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">ta</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">gt</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">lk</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">tcl\-</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">tdg\-</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">tel</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">i</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">m</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">tim\-</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">t\-mo</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">to</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">pl</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">sh</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">ts</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">70</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">m\-</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">m3</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">m5</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">tx\-9</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">up</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">\.b</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">g1</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">si</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">utst</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">v400</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">v750</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">veri</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">vi</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">rg</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">te</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">vk</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">40</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">5</span><span style="color:#8BE9FD">[</span><span style="color:#BD93F9">0-3</span><span style="color:#8BE9FD">]</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">\-v</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">vm40</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">voda</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">vulc</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">vx</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">52</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">53</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">60</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">61</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">70</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">80</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">81</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">83</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">85</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">98</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">w3c</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">\-</span><span style="color:#FF79C6">|</span><span style="color:#FFB86C"> )</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">webc</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">whit</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">wi</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">g </span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">nc</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">nw</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">wmlb</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">wonu</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">x700</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">yas\-</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">your</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">zeto</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">zte\-</span><span style="color:#FF5555">/</span><span style="color:#FF79C6">i</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">test</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#F8F8F2">      a.</span><span style="color:#50FA7B">substr</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">4</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">    )</span></span>
<span class="line"><span style="color:#F8F8F2">  );</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h4>isIOS</h4>
<p>To detect if our user is using an Apple mobile devices, we can test the navigator against the keywords <code>iPad|iPhone|iPod</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> isIOS</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">window </span><span style="color:#FF79C6">||</span><span style="color:#FF79C6"> !</span><span style="color:#F8F8F2">navigator) {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> a </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#F8F8F2">        navigator.userAgent </span><span style="color:#FF79C6">||</span><span style="color:#F8F8F2"> navigator.vendor </span><span style="color:#FF79C6">||</span></span>
<span class="line"><span style="color:#F8F8F2">        (window </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> any</span><span style="color:#F8F8F2">).opera;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#FF5555"> /</span><span style="color:#F1FA8C">iPad</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">iPhone</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">iPod</span><span style="color:#FF5555">/</span><span style="color:#FF79C6">i</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">test</span><span style="color:#F8F8F2">(a);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h4>isIPad</h4>
<p>Likewise, we can reduce the query to <code>iPad</code> only to guess if the user is on an iPad.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> isIPad</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">window </span><span style="color:#FF79C6">||</span><span style="color:#FF79C6"> !</span><span style="color:#F8F8F2">navigator) {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> a </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#F8F8F2">        navigator.userAgent </span><span style="color:#FF79C6">||</span><span style="color:#F8F8F2"> navigator.vendor </span><span style="color:#FF79C6">||</span></span>
<span class="line"><span style="color:#F8F8F2">        (window </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> any</span><span style="color:#F8F8F2">).opera;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#FF5555"> /</span><span style="color:#F1FA8C">iPad</span><span style="color:#FF5555">/</span><span style="color:#FF79C6">i</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">test</span><span style="color:#F8F8F2">(a);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h4>isFirefox</h4>
<p>Likewise, you can detect if the client is using a specific browser as for final example Firefox.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> isFirefox</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">window </span><span style="color:#FF79C6">||</span><span style="color:#FF79C6"> !</span><span style="color:#F8F8F2">navigator) {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> a </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#F8F8F2">        navigator.userAgent </span><span style="color:#FF79C6">||</span><span style="color:#F8F8F2"> navigator.vendor </span><span style="color:#FF79C6">||</span></span>
<span class="line"><span style="color:#F8F8F2">        (window </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> any</span><span style="color:#F8F8F2">).opera;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#FF5555"> /</span><span style="color:#F1FA8C">firefox</span><span style="color:#FF5555">/</span><span style="color:#FF79C6">i</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">test</span><span style="color:#F8F8F2">(a);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>Full screen</h3>
<p>Our presentations can be edited and browse in standalone or in full screen mode, that’s why we have to detect such state. For such purpose, we compare the <code>window.innerHeight</code> to the <code>screen.height</code> , if these are equals, the browser is in full screen mode.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> isFullscreen</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">window </span><span style="color:#FF79C6">||</span><span style="color:#FF79C6"> !</span><span style="color:#F8F8F2">screen) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> window.innerHeight </span><span style="color:#FF79C6">==</span><span style="color:#F8F8F2"> screen.height;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>Remove Attribute From HTML String</h3>
<p>Let say your DOM contains an element which you would like to parse to a <code>string</code> with the help of JavaScript.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> contentEditable</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">true</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> style</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">color: #ff0000</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    Red</span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> div </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> document.</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">div</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">).outerHTML;</span></span></code></pre><p>For some reason, you might not be able to touch or clone the DOM element but would be interested to remove an attribute from <code>string</code> value anyway.</p>
<p>Respectively, you would be interested in such result:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> style</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">color: #ff0000</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Red&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span></code></pre><p>To clean up or remove an attribute from a string you can use the following handy RegExp:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> result </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> div</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">replace</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2">.</span><span style="color:#FF79C6">*?</span><span style="color:#F8F8F2">)(contentEditable</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">""</span></span>
<span class="line"><span style="color:#FF79C6">                       |</span><span style="color:#F8F8F2">contentEditable</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">true</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#FF79C6">                       |</span><span style="color:#F8F8F2">contentEditable</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">false</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#FF79C6">                       |</span><span style="color:#F8F8F2">contentEditable)</span></span>
<span class="line"><span style="color:#F8F8F2">                (.</span><span style="color:#FF79C6">*?></span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2">gi, </span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">$1$3</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span></code></pre><p>Basically, it searches all attribute possibilities and create a new <code>string</code> containing what’s before and after the selection.</p>
<p>My favorite tricks of all of these 😉.</p>
<hr />
<h3>Summary</h3>
<p><a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a> is open source, you can find the above functions in our <a href="https://github.com/deckgo/deckdeckgo/tree/master/utils" rel="noopener noreferrer external">GitHub repo</a> or their related <a href="https://www.npmjs.com/search?q=%40deckdeckgo" rel="noopener noreferrer external">npm packages</a>.</p>
<p>Stay home, stay safe!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[GitHub Actions: Hide And Set Angular Environment Variables]]></title>
          <description><![CDATA[How to hide Angular environments variables from your repo and set these at build time while using GitHub Actions]]></description>
          <link>https://daviddalbusco.com/blog/github-actions-hide-and-set-angular-environment-variables/</link>
          <pubDate>Sat, 11 Apr 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*D8K_z16GbcE3t3YqgvPbEQ.png" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@jae462?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">jae bano</a> on <a href="https://unsplash.com/s/photos/free?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I share <a href="https://daviddalbusco.com/blog/how-to-call-the-service-worker-from-the-web-app-context" rel="noopener noreferrer external">one trick a day</a> until the original scheduled date of the end of the COVID-19 quarantine in Switzerland, April 19th 2020. <strong>Eight</strong> days left until this first milestone. Hopefully better days are ahead.</p>
<hr />
<p>Yesterday I suddenly remembered that I still had to create a <a href="https://github.com/features/actions" rel="noopener noreferrer external">GitHub Actions</a> to build and deploy the editor of our project <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>.</p>
<p>Even though most of the integrations are already automated, this feature is still on my Todo list because I will have to obfuscate some production tokens before being able to properly finish this task.</p>
<p>When I thought about it, I asked my self if I actually had not already solved such feature in another project recently? Guess what, indeed I have 😉, but in an <a href="https://angular.io" rel="noopener noreferrer external">Angular</a> prototype. A small project I developed for my self in order to help me find a flat in Zürich a couple of weeks ago (<a href="https://watamato.com" rel="noopener noreferrer external">Watamato</a> if interested, check it out).</p>
<p>That’s why I am sharing with you today this new tricks.</p>
<hr />
<h3>Concept</h3>
<p>Angular, out of the box, let us handle environments variables thanks to the property <code>fileReplacements</code> of our <code>angular.json</code> . Per default, most probably, your project contains two files, an <code>environment.ts</code> and another one for your productive build, <code>environment.prod.ts</code> .</p>
<p>The idea is the following: In <code>environment.prod.ts</code> we are going to define keys without any values, allowing us to push these in our public <a href="https://github.com" rel="noopener noreferrer external">GitHub</a> repo safely. Then, with the help of system variables, set these before build within our GitHub Actions.</p>
<hr />
<h3>Setup Environment.ts</h3>
<p>To begin with, let’s setup first our <code>environment.ts</code> files. Our goal is to obfuscate a token, let’s say for example that we want to hide our <a href="https://firebase.google.com" rel="noopener noreferrer external">Firebase</a> Api key.</p>
<p>Not really related to the solution but let’s say a goodie, we also inject the <code>version</code> and <code>name</code> of our application in your configuration. Note that this requires the activation of the compiler options <code>resolveJsonModule</code> to <code>true</code> in your <code>tsconfig.json.</code></p>
<p>Our <code>environment.ts</code> :</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { name, version } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">../../package.json</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> environment </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    production</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    firebase</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        apiKey</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">the-key-you-can-expose</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    },</span></span>
<span class="line"><span style="color:#F8F8F2">    name,</span></span>
<span class="line"><span style="color:#F8F8F2">    version</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>And our <code>environment.prod.ts</code> which contains <code>'undefined'</code> for the hidden value. The reason behind this being a string is the fact that our upcoming parser is going to inject such value if the key is not defined at build time.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> environment </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    production</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    firebase</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        apiKey</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">undefined</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    },</span></span>
<span class="line"><span style="color:#F8F8F2">    name</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">enviro-replace</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    version</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">0.0.1</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><hr />
<h4>Hide Development Variables</h4>
<p>In the previous setting, I amend the fact that we are agree to expose our key in our development configuration, but you might also want to hide it. In such case, what I recommend, is extracting the values in a separate local file which you explicitly ignore in your <code>.gitignore</code>.</p>
<p>For example, let’s say we create a new file <code>firebase.environment.ts</code> in which we move our configuration and which add to the list of Git ignored files.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> firebase </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    firebase</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        apiKey</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">the-key-you-can-expose</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Then we can update our <code>environment.ts</code> as following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { firebase } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./firebase.environment</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { name, version } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">../../package.json</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> environment </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    production</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#FF79C6">    ...</span><span style="color:#F8F8F2">firebase,</span></span>
<span class="line"><span style="color:#F8F8F2">    name,</span></span>
<span class="line"><span style="color:#F8F8F2">    version</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><hr />
<h3>Update Variables Before Build</h3>
<p>Our productive environment contains at this point an hidden value <code>'undefined'</code> which we have to replace before building our application.</p>
<p>For such purpose we can use the “magic file” described in the <a href="https://medium.com/@ferie/how-to-pass-environment-variables-at-building-time-in-an-angular-application-using-env-files-4ae1a80383c" rel="noopener noreferrer external">article</a> of <a href="https://twitter.com/Ferie80" rel="noopener noreferrer external">Riccardo Andreatta</a> 👍.</p>
<p>We create a new script <code>./config.index.ts</code> . Basically what it does is overwriting our <code>environment.prod.ts</code> file with new values and notably these we are going to define in your environment or GiHub Actions secret store.</p>
<p>In this parser we notice two interesting things:</p>
<ol>
<li>It contains the environment variables too. That means that if you would add a new key to your configuration, you will have to update the script too.</li>
<li>We are using the environment process <code>process.env.FIREBASE_API_KEY</code> to inject a value we would path from our environment or from GitHub Actions to overwrite the environment with the effective key we were looking to hide.</li>
</ol>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { writeFile } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">fs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { name, version } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">../package.json</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> targetPath </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./src/environments/environment.prod.ts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> envConfigFile </span><span style="color:#FF79C6">=</span><span style="color:#F1FA8C"> `export const environment = {</span></span>
<span class="line"><span style="color:#F1FA8C">   production: true,</span></span>
<span class="line"><span style="color:#F1FA8C">   firebase: {</span></span>
<span class="line"><span style="color:#F1FA8C">        apiKey: '</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">process.env.</span><span style="color:#BD93F9">FIREBASE_API_KEY</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">'</span></span>
<span class="line"><span style="color:#F1FA8C">    },</span></span>
<span class="line"><span style="color:#F1FA8C">    name: '</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">name</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">',</span></span>
<span class="line"><span style="color:#F1FA8C">    version: '</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">version</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">'</span></span>
<span class="line"><span style="color:#F1FA8C">};</span></span>
<span class="line"><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">writeFile</span><span style="color:#F8F8F2">(targetPath, envConfigFile, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">utf8</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, (</span><span style="color:#FFB86C;font-style:italic">err</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(err);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><p>Finally we can add the execution of the script to our <code>package.json</code> :</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#E9F284">"</span><span style="color:#F1FA8C">scripts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">: {</span></span>
<span class="line"><span style="color:#8BE9FE">  "</span><span style="color:#8BE9FD">config</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#E9F284">     "</span><span style="color:#F1FA8C">ts-node -O '{</span><span style="color:#FF79C6">\"</span><span style="color:#F1FA8C">module</span><span style="color:#FF79C6">\"</span><span style="color:#F1FA8C">: </span><span style="color:#FF79C6">\"</span><span style="color:#F1FA8C">commonjs</span><span style="color:#FF79C6">\"</span><span style="color:#F1FA8C">}' ./config.index.ts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">  "</span><span style="color:#8BE9FD">build</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">npm run config &#x26;&#x26; ng build --prod</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>Testing</h3>
<p>We are all set, we can now give it a try. Let’s first run a build without doing anything.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*JMobhDqd7gnda8JQ1etzQw.png" alt="" / loading="lazy"></p>
<p>As you can notice, our <code>apiKey</code> remains equals to <code>'undefined'</code> and therefor not valid for our build.</p>
<p>Let’s now try to define an environment variable (<code>export FIREBASE_API_KEY=&quot;this is my prod key&quot;</code>) and run our build again.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*BU5qNiYe0mr3JcjdUHc9bQ.png" alt="" / loading="lazy"></p>
<p>Tada, our environment variable has been set and use for our build 🎉.</p>
<p>At this point you may ask yourself “yes but David, if we do so, then each time we run a build our <code>environment.prod.ts</code> file is going to be modified”. To which I would answer “yes you are right … but our goal is to automate the build with a GitHub Actions in order to not run productive build locally anymore, therefore the modification is not that a problem for our daily workflow 😇”.</p>
<hr />
<h3>GitHub Actions</h3>
<p>The very final piece, the automation with GitHub Actions.</p>
<p>I am not going to cover how is it possible to create such script, <a href="https://twitter.com/julienrenaux" rel="noopener noreferrer external">Julien Renaux</a> covers well the subject in one of his <a href="https://julienrenaux.fr/2019/11/25/building-deploying-stenciljs-apps-firebase-hosting-github-actions/" rel="noopener noreferrer external">blog post</a> or alternatively you can check out of my Angular related <a href="https://github.com/peterpeterparker/watamato/blob/master/.github/workflows/app.yml" rel="noopener noreferrer external">app.yml</a> GitHub actions.</p>
<p>I assume that your script is ready and that you have defined a <code>FIREBASE_API_KEY</code> in your repos’ secrets.</p>
<p>The related build sequence of your application probably looks like the following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#8BE9FD">jobs</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">  build</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">    name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> Build</span></span>
<span class="line"><span style="color:#8BE9FD">    runs-on</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> ubuntu-latest</span></span>
<span class="line"><span style="color:#8BE9FD">    steps</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#FF79C6">      -</span><span style="color:#8BE9FD"> name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> Checkout Repo</span></span>
<span class="line"><span style="color:#8BE9FD">        uses</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> actions/checkout@master</span></span>
<span class="line"><span style="color:#FF79C6">      -</span><span style="color:#8BE9FD"> name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> Install Dependencies</span></span>
<span class="line"><span style="color:#8BE9FD">        run</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> npm ci</span></span>
<span class="line"><span style="color:#FF79C6">      -</span><span style="color:#8BE9FD"> name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> Build</span></span>
<span class="line"><span style="color:#8BE9FD">        run</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> npm run build</span></span></code></pre><p>To which we now “only” need to add the following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#8BE9FD">jobs</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">  build</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">    name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> Build</span></span>
<span class="line"><span style="color:#8BE9FD">    runs-on</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> ubuntu-latest</span></span>
<span class="line"><span style="color:#8BE9FD">    steps</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#FF79C6">      -</span><span style="color:#8BE9FD"> name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> Checkout Repo</span></span>
<span class="line"><span style="color:#8BE9FD">        uses</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> actions/checkout@master</span></span>
<span class="line"><span style="color:#FF79C6">      -</span><span style="color:#8BE9FD"> name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> Install Dependencies</span></span>
<span class="line"><span style="color:#8BE9FD">        run</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> npm ci</span></span>
<span class="line"><span style="color:#FF79C6">      -</span><span style="color:#8BE9FD"> name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> Build</span></span>
<span class="line"><span style="color:#8BE9FD">        run</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> npm run build</span></span>
<span class="line"><span style="color:#8BE9FD">        env</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">          FIREBASE_API_KEY</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> ${{ secrets.FIREBASE_API_KEY }}</span></span></code></pre><p>That’s already it. Doing so, GitHub Actions will set the related environment variable for our build and our above script and configuration will take care of the rest.</p>
<hr />
<h3>Summary</h3>
<p>GitHub Actions are so handy, there were and are a big asset to my continuous integration workflow.</p>
<p>Stay home, stay safe!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[React, Web Workers, IndexedDB and ExcelJS]]></title>
          <description><![CDATA[How to generate an Excel spreadsheet with IndexedDB data and a Web Worker within a React application]]></description>
          <link>https://daviddalbusco.com/blog/react-web-workers-indexeddb-and-exceljs/</link>
          <pubDate>Fri, 10 Apr 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*KfXCp8tFOBqrJDS6BUIkYQ.png" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@danielcgold?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Dan Gold</a> on <a href="https://unsplash.com/s/photos/free?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I share <a href="https://daviddalbusco.com/blog/how-to-call-the-service-worker-from-the-web-app-context" rel="noopener noreferrer external">one trick a day</a> until the original scheduled date of the end of the COVID-19 quarantine in Switzerland, April 19th 2020. <strong>Nine</strong> days left until this first milestone. Hopefully better days are ahead.</p>
<hr />
<p>In previous blog posts, I shared how <a href="https://daviddalbusco.com/blog/react-and-web-workers" rel="noopener noreferrer external">React and Web Workers can interact</a> and how they can <a href="https://daviddalbusco.com/blog/react-web-workers-and-indexeddb" rel="noopener noreferrer external">use data stored in IndexedDB</a>.</p>
<p>I learned these tricks when I was developing <a href="https://tietracker.app.link/" rel="noopener noreferrer external">Tie Tracker</a>, a simple, open source and free time tracking app ⏱.</p>
<p>In this application, I use such features to notably generate Excel spreadsheet containing the user’s entries.</p>
<hr />
<h3>User Interface</h3>
<p>Regarding previous user interface we have developed in the series, we are still going to stick to a “Tomato counter”. Only changes we apply regarding user interaction is the fact that instead of calling a &quot;sum function&quot;, we are going to call our Web Worker in order to generate an Excel spreadsheet.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    IonContent,</span></span>
<span class="line"><span style="color:#F8F8F2">    IonPage,</span></span>
<span class="line"><span style="color:#F8F8F2">    IonLabel,</span></span>
<span class="line"><span style="color:#F8F8F2">    IonButton</span></span>
<span class="line"><span style="color:#F8F8F2">} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@ionic/react</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> React, {useEffect, useState} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {RouteComponentProps} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">react-router</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./Page.css</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {set} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">idb-keyval</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> Page</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> React</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">FC</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">RouteComponentProps</span><span style="color:#F8F8F2">&#x3C;{ name</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">; }>> </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> ({</span><span style="color:#FFB86C;font-style:italic">match</span><span style="color:#F8F8F2">}) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> [countTomato, setCountTomato] </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useState</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">number</span><span style="color:#F8F8F2">>(</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> exportWorker</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Worker</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Worker</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">./workers/export.js</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    useEffect</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        exportWorker.</span><span style="color:#50FA7B">onmessage</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">$event</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> MessageEvent</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">            if</span><span style="color:#F8F8F2"> ($event </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> $event.data) {</span></span>
<span class="line"><span style="color:#50FA7B">                download</span><span style="color:#F8F8F2">($event.data);</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"><span style="color:#F8F8F2">        };</span></span>
<span class="line"><span style="color:#F8F8F2">    }, [exportWorker]);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    useEffect</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">        incTomato</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    }, [countTomato]);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> incTomato</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (countTomato </span><span style="color:#FF79C6">></span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">            await</span><span style="color:#50FA7B"> set</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`tomato</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">countTomato</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">, countTomato);</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    function</span><span style="color:#50FA7B"> doExportToExcel</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#F8F8F2">        exportWorker</span></span>
<span class="line"><span style="color:#F8F8F2">            .</span><span style="color:#50FA7B">postMessage</span><span style="color:#F8F8F2">({msg</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">export</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">});</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    function</span><span style="color:#50FA7B"> download</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">blob</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Blob</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#6272A4">        // TODO: See last chapter</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonPage</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonContent</span><span style="color:#50FA7B;font-style:italic"> className</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ion-padding</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonLabel</span><span style="color:#F8F8F2">>Tomato: </span><span style="color:#FF79C6">{</span><span style="color:#F8F8F2">countTomato</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonLabel</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> className</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ion-padding-top</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonButton</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                     onClick</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> setCountTomato</span><span style="color:#F8F8F2">(countTomato </span><span style="color:#FF79C6">+</span><span style="color:#BD93F9"> 1</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                     color</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">primary</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Tomato&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonButton</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonButton</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                     onClick</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> doExportToExcel</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                     color</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">secondary</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Export&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonButton</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonContent</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonPage</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> Page;</span></span></code></pre><p>At this point it does not do much because the Web Worker to handle the “Export” action is not yet ready and we are also not yet using its potential result, but, it should looks like the following.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*-nowDWxwoaRF8H2TUuLRMw.gif" alt="" / loading="lazy"></p>
<hr />
<h3>Web Worker</h3>
<p>Before implementing anything, we create a new almost empty Web Worker <code>./public/workers/export.js</code> which only takes care of handling a message “export”, the one we pass to start the process in this separate thread.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">self.</span><span style="color:#50FA7B">onmessage</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">$event</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> ($event </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> $event.data </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> $event.data.msg </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">export</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> data </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> generateExcel</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">        self.</span><span style="color:#50FA7B">postMessage</span><span style="color:#F8F8F2">(data);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> generateExcel</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9"> null</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Afterwards, we add <a href="https://github.com/jakearchibald/idb-keyval" rel="noopener noreferrer external">idb-keyval</a>, my favorite library to interact with IndexedDB, and only call the function to list the <code>keys()</code> present in the database, keys which are generated by our above component on user interaction respectively each time the user increment the “tomato counter”.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">importScripts</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://unpkg.com/idb-keyval@latest/dist/idb-keyval-iife.min.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">self.</span><span style="color:#50FA7B">onmessage</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">$event</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> ($event </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> $event.data </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> $event.data.msg </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">export</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> data </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> generateExcel</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">        self.</span><span style="color:#50FA7B">postMessage</span><span style="color:#F8F8F2">(data);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> generateExcel</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> keys </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> idbKeyval.</span><span style="color:#50FA7B">keys</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9"> null</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>ExcelJS</h3>
<p>There are a couple of libraries which help read, write and manipulate spreadsheet data. I selected <a href="https://github.com/exceljs/exceljs" rel="noopener noreferrer external">ExcelJS</a> and I think it did the job pretty well, that’s why we are using it in this tutorial too.</p>
<p>As previously for idb-keyval, we can import the dependency in our worker from <a href="https://unpkg.com" rel="noopener noreferrer external">Unpkg</a>.</p>
<p>In our function to generate our spreadsheet, we create a new <code>ExcelJS.Workbook</code> object and define some attributes.</p>
<p>An Excel file can contain multiple sheets, that why we then create such working page.</p>
<p>We skip, until next step, the preparation of the data themselves and we are implementing the generation of the spreadsheet. For such purpose, ExcelJS exposes a function <code>writeToBuffer</code> to generate the data to, well, a buffer which we are using to generate finally a <code>blob</code> (which will ultimately contains our spreadsheet).</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">importScripts</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://unpkg.com/idb-keyval@latest/dist/idb-keyval-iife.min.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#50FA7B">importScripts</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://unpkg.com/exceljs@latest/dist/exceljs.min.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">self.</span><span style="color:#50FA7B">onmessage</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">$event</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> ($event </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> $event.data </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> $event.data.msg </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">export</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> data </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> generateExcel</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">        self.</span><span style="color:#50FA7B">postMessage</span><span style="color:#F8F8F2">(data);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> generateExcel</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> workbook </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#F8F8F2"> ExcelJS.</span><span style="color:#50FA7B">Workbook</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    workbook.creator </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Tomato</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    workbook.lastModifiedBy </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Tomato</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    workbook.created </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Date</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    workbook.modified </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Date</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // Force workbook calculation on load</span></span>
<span class="line"><span style="color:#F8F8F2">    workbook.calcProperties.fullCalcOnLoad </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> worksheet </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> workbook.</span><span style="color:#50FA7B">addWorksheet</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Tomato page 1</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, {</span></span>
<span class="line"><span style="color:#F8F8F2">        properties</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { tabColor</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { argb</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">#FF0000</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> } },</span></span>
<span class="line"><span style="color:#F8F8F2">        pageSetup</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { paperSize</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 9</span><span style="color:#F8F8F2">, orientation</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">landscape</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> }</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#6272A4">    // TODO Prepare the data table</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> buf </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> workbook.xlsx.</span><span style="color:#50FA7B">writeBuffer</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Blob</span><span style="color:#F8F8F2">([buf], {</span></span>
<span class="line"><span style="color:#F8F8F2">        type</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">application/vnd.openxmlformats-officedocument.spreadsheetml.sheet</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>We are going to add a table to our spreadsheet.</p>
<p>To do so, we have to provide an array which should contain an array for each line to print 🤔.</p>
<p>Let’s say for example that our data to display are displayed with five rows of two columns, this means that the overall array should contain five elements and each of them should contain two.</p>
<p>Still confuse? I guess the code will speak for itself 😅.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> keys </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> idbKeyval.</span><span style="color:#50FA7B">keys</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> data </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> [];</span></span>
<span class="line"><span style="color:#FF79C6">for</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> key </span><span style="color:#FF79C6">of</span><span style="color:#F8F8F2"> keys) {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> value </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> idbKeyval.</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(key);</span></span>
<span class="line"><span style="color:#F8F8F2">    data.</span><span style="color:#50FA7B">push</span><span style="color:#F8F8F2">([</span><span style="color:#F1FA8C">`Counter </span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">value</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">, value]);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>The data being prepared, we can finally add the table. It contains a name, a page reference, some options, the definitions of the columns and their options and finally the data we just prepared above.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">worksheet.</span><span style="color:#50FA7B">addTable</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    name</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Tomatoes</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    ref</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">A1</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    headerRow</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    totalsRow</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    style</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        theme</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">TableStyleLight1</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        showRowStripes</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span></span>
<span class="line"><span style="color:#F8F8F2">    },</span></span>
<span class="line"><span style="color:#F8F8F2">    columns</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#F8F8F2">        { name</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Label</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, filterButton</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">, totalsRowLabel</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> ""</span><span style="color:#F8F8F2"> },</span></span>
<span class="line"><span style="color:#F8F8F2">        { name</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Count</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, totalsRowFunction</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">sum</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> }</span></span>
<span class="line"><span style="color:#F8F8F2">    ],</span></span>
<span class="line"><span style="color:#F8F8F2">    rows</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> data</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><p>That’s it, our worker is ready. Altogether it looks like the following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">importScripts</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://unpkg.com/idb-keyval@latest/dist/idb-keyval-iife.min.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#50FA7B">importScripts</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://unpkg.com/exceljs@latest/dist/exceljs.min.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">self.</span><span style="color:#50FA7B">onmessage</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">$event</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> ($event </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> $event.data </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> $event.data.msg </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">export</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> data </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> generateExcel</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">        self.</span><span style="color:#50FA7B">postMessage</span><span style="color:#F8F8F2">(data);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> generateExcel</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> workbook </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#F8F8F2"> ExcelJS.</span><span style="color:#50FA7B">Workbook</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    workbook.creator </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Tomato</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    workbook.lastModifiedBy </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Tomato</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    workbook.created </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Date</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    workbook.modified </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Date</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // Force workbook calculation on load</span></span>
<span class="line"><span style="color:#F8F8F2">    workbook.calcProperties.fullCalcOnLoad </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> worksheet </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> workbook.</span><span style="color:#50FA7B">addWorksheet</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Tomato page 1</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, {</span></span>
<span class="line"><span style="color:#F8F8F2">        properties</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { tabColor</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { argb</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">#FF0000</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> } },</span></span>
<span class="line"><span style="color:#F8F8F2">        pageSetup</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { paperSize</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 9</span><span style="color:#F8F8F2">, orientation</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">landscape</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> }</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> keys </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> idbKeyval.</span><span style="color:#50FA7B">keys</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> data </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> [];</span></span>
<span class="line"><span style="color:#FF79C6">    for</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> key </span><span style="color:#FF79C6">of</span><span style="color:#F8F8F2"> keys) {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> value </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> idbKeyval.</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(key);</span></span>
<span class="line"><span style="color:#F8F8F2">        data.</span><span style="color:#50FA7B">push</span><span style="color:#F8F8F2">([</span><span style="color:#F1FA8C">`Counter </span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">value</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">, value]);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    worksheet.</span><span style="color:#50FA7B">addTable</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        name</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Tomatoes</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        ref</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">A1</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        headerRow</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        totalsRow</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        style</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">            theme</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">TableStyleLight1</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">            showRowStripes</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span></span>
<span class="line"><span style="color:#F8F8F2">        },</span></span>
<span class="line"><span style="color:#F8F8F2">        columns</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#F8F8F2">            { name</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Label</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, filterButton</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">, totalsRowLabel</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> ""</span><span style="color:#F8F8F2"> },</span></span>
<span class="line"><span style="color:#F8F8F2">            { name</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Count</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, totalsRowFunction</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">sum</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> }</span></span>
<span class="line"><span style="color:#F8F8F2">        ],</span></span>
<span class="line"><span style="color:#F8F8F2">        rows</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> data</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> buf </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> workbook.xlsx.</span><span style="color:#50FA7B">writeBuffer</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Blob</span><span style="color:#F8F8F2">([buf], {</span></span>
<span class="line"><span style="color:#F8F8F2">        type</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">application/vnd.openxmlformats-officedocument.spreadsheetml.sheet</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>Download</h3>
<p>If you try our solution, you may notice at this point that nothing is still happening and you are right, nothing is happening from a user perspective.</p>
<p>Even though we have implemented the user interaction, chained both Web Worker and spreadsheet generation, we are not yet interpreting the result, the buffer we have transformed to a blob.</p>
<p>That’s why the last piece of this implementation is the function <code>download()</code> we did not have so far implemented in our component.</p>
<p>Credits to the followings go to <a href="https://stackoverflow.com/users/600135/kol" rel="noopener noreferrer external">kol</a> with his answer on <a href="https://stackoverflow.com/questions/19327749/javascript-blob-filename-without-link/19328891#19328891" rel="noopener noreferrer external">Stackoverflow</a>. Not all heroes wear capes 🙏.</p>
<p>The function is adding an hidden link to the DOM which contains our blob, our spreadsheet, as target. Within the same function, we are calling it to trigger the download and are removing the element from the DOM afterwards.</p>
<p>Note that the browser will notice that it has to handle the URL in such way as we have created a blob with the specific type <code>application/vnd.openxmlformats-officedocument.spreadsheetml.sheet</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">function</span><span style="color:#50FA7B"> download</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">blob</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Blob</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> a</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLAnchorElement</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> document.</span><span style="color:#50FA7B">createElement</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">a</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    a.style.display </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">none</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    document.body.</span><span style="color:#50FA7B">appendChild</span><span style="color:#F8F8F2">(a);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> url</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> window.</span><span style="color:#BD93F9">URL</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">createObjectURL</span><span style="color:#F8F8F2">(blob);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    a.href </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> url;</span></span>
<span class="line"><span style="color:#F8F8F2">    a.download </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">tomato.xlsx</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    a.</span><span style="color:#50FA7B">click</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    window.</span><span style="color:#BD93F9">URL</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">revokeObjectURL</span><span style="color:#F8F8F2">(url);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (a </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> a.parentElement) {</span></span>
<span class="line"><span style="color:#F8F8F2">        a.parentElement.</span><span style="color:#50FA7B">removeChild</span><span style="color:#F8F8F2">(a);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>And voilà, our Excel spreadsheet generated with a Web Worker using ExcelJS is downloaded 🎉.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*9M_Bym_ORTCaMQx1P0kUdg.gif" alt="" / loading="lazy"></p>
<p>In case you would need it, here is the component enhanced with the <code>download</code> function.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    IonContent,</span></span>
<span class="line"><span style="color:#F8F8F2">    IonPage,</span></span>
<span class="line"><span style="color:#F8F8F2">    IonLabel,</span></span>
<span class="line"><span style="color:#F8F8F2">    IonButton</span></span>
<span class="line"><span style="color:#F8F8F2">} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@ionic/react</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> React, {useEffect, useState} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {RouteComponentProps} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">react-router</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./Page.css</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {set} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">idb-keyval</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> Page</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> React</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">FC</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">RouteComponentProps</span><span style="color:#F8F8F2">&#x3C;{ name</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">; }>> </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> ({</span><span style="color:#FFB86C;font-style:italic">match</span><span style="color:#F8F8F2">}) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> [countTomato, setCountTomato] </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useState</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">number</span><span style="color:#F8F8F2">>(</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> exportWorker</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Worker</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Worker</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">./workers/export.js</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    useEffect</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        exportWorker.</span><span style="color:#50FA7B">onmessage</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">$event</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> MessageEvent</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">            if</span><span style="color:#F8F8F2"> ($event </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> $event.data) {</span></span>
<span class="line"><span style="color:#50FA7B">                download</span><span style="color:#F8F8F2">($event.data);</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"><span style="color:#F8F8F2">        };</span></span>
<span class="line"><span style="color:#F8F8F2">    }, [exportWorker]);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    useEffect</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">        incTomato</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    }, [countTomato]);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> incTomato</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (countTomato </span><span style="color:#FF79C6">></span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">            await</span><span style="color:#50FA7B"> set</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`tomato</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">countTomato</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">, countTomato);</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    function</span><span style="color:#50FA7B"> doExportToExcel</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#F8F8F2">        exportWorker</span></span>
<span class="line"><span style="color:#F8F8F2">            .</span><span style="color:#50FA7B">postMessage</span><span style="color:#F8F8F2">({msg</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">export</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">});</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    function</span><span style="color:#50FA7B"> download</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">blob</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Blob</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> a</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLAnchorElement</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> document.</span><span style="color:#50FA7B">createElement</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">a</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">        a.style.display </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">none</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        document.body.</span><span style="color:#50FA7B">appendChild</span><span style="color:#F8F8F2">(a);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> url</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> window.</span><span style="color:#BD93F9">URL</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">createObjectURL</span><span style="color:#F8F8F2">(blob);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        a.href </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> url;</span></span>
<span class="line"><span style="color:#F8F8F2">        a.download </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">tomato.xlsx</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        a.</span><span style="color:#50FA7B">click</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        window.</span><span style="color:#BD93F9">URL</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">revokeObjectURL</span><span style="color:#F8F8F2">(url);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (a </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> a.parentElement) {</span></span>
<span class="line"><span style="color:#F8F8F2">            a.parentElement.</span><span style="color:#50FA7B">removeChild</span><span style="color:#F8F8F2">(a);</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonPage</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonContent</span><span style="color:#50FA7B;font-style:italic"> className</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ion-padding</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonLabel</span><span style="color:#F8F8F2">>Tomato: </span><span style="color:#FF79C6">{</span><span style="color:#F8F8F2">countTomato</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonLabel</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> className</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ion-padding-top</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonButton</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                     onClick</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> setCountTomato</span><span style="color:#F8F8F2">(countTomato </span><span style="color:#FF79C6">+</span><span style="color:#BD93F9"> 1</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                     color</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">primary</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Tomato&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonButton</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonButton</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                     onClick</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> doExportToExcel</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                     color</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">secondary</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Export&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonButton</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonContent</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonPage</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> Page;</span></span></code></pre><hr />
<h3>Summary</h3>
<p>Who would have thought that generating Excel spreadsheet can be fun 😉?</p>
<p>Stay home, stay safe!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[React, Web Workers and IndexedDB]]></title>
          <description><![CDATA[How to use IndexedDB in React applications with Web Workers]]></description>
          <link>https://daviddalbusco.com/blog/react-web-workers-and-indexeddb/</link>
          <pubDate>Thu, 09 Apr 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*Ih6g24vTLWb_Zl-yiNOuNA.png" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@pawankawan?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Pawan Kawan</a> on <a href="https://unsplash.com/s/photos/free?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I share <a href="https://daviddalbusco.com/blog/how-to-call-the-service-worker-from-the-web-app-context" rel="noopener noreferrer external">one trick a day</a> until the original scheduled date of the end of the COVID-19 quarantine in Switzerland, April 19th 2020. <strong>Ten</strong> days left until this first milestone. Hopefully better days are ahead.</p>
<hr />
<p>In a <a href="https://daviddalbusco.com/blog/react-and-web-workers" rel="noopener noreferrer external">previous blog post</a> of this series I shared my solution to make <a href="https://reactjs.org" rel="noopener noreferrer external">React</a> and <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers" rel="noopener noreferrer external">Web Workers</a> interacts. A tricks I experimented while developing <a href="https://tietracker.app.link/" rel="noopener noreferrer external">Tie Tracker</a>, a simple, open source and free time tracking app ⏱.</p>
<p>Another, I hope, interesting features of such construct and this app was the idea of handling data in the threads using <a href="https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API" rel="noopener noreferrer external">IndexedDB</a>.</p>
<p>The idea was simple: letting the user enter and modify data in the database on the application side (JavaScript mono thread), because such operation takes few time, but to defer every calculations or statistics to the Web Workers in order to not block the user interface and interaction.</p>
<p>That is why I am sharing with you this recipe in this follow-up article 😁.</p>
<hr />
<h3>idb-keyval</h3>
<p>I am a bit picky when it comes to third party libraries because I am a bit “bundlephobic” but when it comes to interacting with the IndexedDB there are no hesitations, <a href="https://github.com/jakearchibald/idb-keyval" rel="noopener noreferrer external">idb-keyval</a> from <a href="https://twitter.com/jaffathecake" rel="noopener noreferrer external">Jake Archibald</a> is my go-to-library.</p>
<p>Less than 600 bytes, tree-shaking friendly, promises based ... stop right there, I am all in!</p>
<p>Therefore of course in this solution we are going to use it 😉.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> i</span><span style="color:#F1FA8C"> idb-keyval</span><span style="color:#BD93F9"> --save</span></span></code></pre><hr />
<h3>User Interface</h3>
<p>In the previous post we had a “Tomato and Apple counters”. I propose that we now concentrate our self on the “Tomato” one and that we try to defer the calculation of the sum of the counter to the Web Worker.</p>
<p>Before any interaction with IndexedDB, our modified component’s code looks like the following.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    IonContent,</span></span>
<span class="line"><span style="color:#F8F8F2">    IonPage,</span></span>
<span class="line"><span style="color:#F8F8F2">    IonLabel,</span></span>
<span class="line"><span style="color:#F8F8F2">    IonButton</span></span>
<span class="line"><span style="color:#F8F8F2">} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@ionic/react</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> React, {useEffect, useState} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {RouteComponentProps} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">react-router</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./Page.css</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> Page</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> React</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">FC</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">RouteComponentProps</span><span style="color:#F8F8F2">&#x3C;{ name</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">; }>> </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> ({</span><span style="color:#FFB86C;font-style:italic">match</span><span style="color:#F8F8F2">}) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> [countTomato, setCountTomato] </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useState</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">number</span><span style="color:#F8F8F2">>(</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> [sumTomato, setSumApple] </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useState</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">number</span><span style="color:#F8F8F2">>(</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> tomatoWorker</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Worker</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Worker</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">./workers/tomato.js</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    useEffect</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        tomatoWorker.</span><span style="color:#50FA7B">onmessage</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">$event</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> MessageEvent</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">            if</span><span style="color:#F8F8F2"> ($event </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> $event.data) {</span></span>
<span class="line"><span style="color:#50FA7B">                setSumApple</span><span style="color:#F8F8F2">($event.data);</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"><span style="color:#F8F8F2">        };</span></span>
<span class="line"><span style="color:#F8F8F2">    }, [tomatoWorker]);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    function</span><span style="color:#50FA7B"> doSumTomato</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#F8F8F2">        tomatoWorker</span></span>
<span class="line"><span style="color:#F8F8F2">         .</span><span style="color:#50FA7B">postMessage</span><span style="color:#F8F8F2">({msg</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">sumTomato</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">});</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonPage</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonContent</span><span style="color:#50FA7B;font-style:italic"> className</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ion-padding</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonLabel</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                   Tomato: </span><span style="color:#FF79C6">{</span><span style="color:#F8F8F2">countTomato</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2"> | Sum: </span><span style="color:#FF79C6">{</span><span style="color:#F8F8F2">sumTomato</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonLabel</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> className</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ion-padding-top</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonButton</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                        onClick</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span></span>
<span class="line"><span style="color:#50FA7B">                          setCountTomato</span><span style="color:#F8F8F2">(countTomato </span><span style="color:#FF79C6">+</span><span style="color:#BD93F9"> 1</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                        color</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">primary</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Tomato&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonButton</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonButton</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                        onClick</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> doSumTomato</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                        color</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">secondary</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Sum now!&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonButton</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonContent</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonPage</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> Page;</span></span></code></pre><p>Of course as we do not have yet implemented the sum part, the Web Worker, it does not do much.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*0Qz6Y3scEzimJ--S7TTNgA.gif" alt="" / loading="lazy"></p>
<hr />
<h3>User Interaction</h3>
<p>Our goal is to write a data in the IndexDB on user interaction, that’s why for demo purpose and for fun, I suggest that we generate a new entry in the database each time the tomato counter is incremented. For this purpose, we register a new <code>useEffect</code> to <code>set</code> entries.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { set } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">idb-keyval</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">useEffect</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    incTomato</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">}, [countTomato]);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> incTomato</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (countTomato </span><span style="color:#FF79C6">></span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        await</span><span style="color:#50FA7B"> set</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`tomato</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">countTomato</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">, countTomato);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>And that’s already it. Every time the counter is incremented, the effect is triggered and in extension we are using idb-keyval to add a value in the database.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*Upl2MW8HhjDarFAcOoOdUQ.gif" alt="" / loading="lazy"></p>
<hr />
<h3>Web Workers</h3>
<p>For this tutorial I created a new worker file <code>./public/workers/tomato.js</code> which before any IndexDB interaction looks like the following.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">self.</span><span style="color:#50FA7B">onmessage</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">$event</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> ($event </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> $event.data </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> $event.data.msg </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">sumTomato</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> sum </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> sumTomato</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">        self.</span><span style="color:#50FA7B">postMessage</span><span style="color:#F8F8F2">(sum);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> sumTomato</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#6272A4">    // TODO sum tomato</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>We have now to access our data in IndexedDB. To solve this problem we have two options, either code everything or use a library. As a big fan of idb-keyval, I would like to use it here too.</p>
<p>Unfortunately, our Web Workers are shipped as assets and therefore don’t have access to our bundle and its dependencies. That’s why we have to perform a setup and import a script in our workers with the help of <code>importScripts</code> .</p>
<p>I’m guessing that one nice way would be to handle this dependency with Rollup or Webpack, probably through plugins, but I have to say I did not followed that path.</p>
<p>That’s why we have two options remaining. Either link an external script or download it, place it in the same folder and reference it locally.</p>
<p>If you would like to follow the “local way”, your import would look like the following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">importScripts</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">./idb-keyval-iife.min.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span></code></pre><p>Or, as we are going to do, here’s how we can import it using <a href="https://unpkg.com/" rel="noopener noreferrer external">Unpkg</a>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">importScripts</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://unpkg.com/idb-keyval@latest/dist/idb-keyval-iife.min.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span></code></pre><p>All set, we can now enjoy idb-keyval and access our data in the IndexedDB from our Web Worker too. As for example, we can list the <code>keys()</code> present on the database, iterate on these to <code>get(key)</code> their values and calculate a pseudo sum.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">importScripts</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://unpkg.com/idb-keyval@latest/dist/idb-keyval-iife.min.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">self.</span><span style="color:#50FA7B">onmessage</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">$event</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> ($event </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> $event.data </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> $event.data.msg </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">sumTomato</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> sum </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> sumTomato</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">        self.</span><span style="color:#50FA7B">postMessage</span><span style="color:#F8F8F2">(sum);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> sumTomato</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> keys </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> idbKeyval.</span><span style="color:#50FA7B">keys</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> sum </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">    for</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> key </span><span style="color:#FF79C6">of</span><span style="color:#F8F8F2"> keys) {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> value </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> idbKeyval.</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(key);</span></span>
<span class="line"><span style="color:#F8F8F2">        sum </span><span style="color:#FF79C6">+=</span><span style="color:#F8F8F2"> value;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> sum;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>And voilà 🎉. We are using IndexedDB in all our threads 😃.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*o4uSCTJUb9X253cdPmJJMA.gif" alt="" / loading="lazy"></p>
<hr />
<h3>Summary</h3>
<p>The web is so much fun.</p>
<p>Stay home, stay safe.</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Angular Testing: Mock Private Functions]]></title>
          <description><![CDATA[How to mock a private function in your automated Angular tests with Jasmine or Jest]]></description>
          <link>https://daviddalbusco.com/blog/angular-testing-mock-private-functions/</link>
          <pubDate>Wed, 08 Apr 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*hVf0bMRV3eeYXaj6D2ZJJg.png" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@overture_creations?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Overture Creations</a> on <a href="https://unsplash.com/s/photos/free?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I share <a href="https://daviddalbusco.com/blog/how-to-call-the-service-worker-from-the-web-app-context" rel="noopener noreferrer external">one trick a day</a> until (probably not) the end of the COVID-19 quarantine in Switzerland, April 19th 2020. <strong>Eleven</strong> days left until hopefully better days.</p>
<hr />
<p>This week I made several progress in one of my client’s project and had therefore to write new test cases. For one of these, I notably had to mock a private function using <a href="https://jestjs.io" rel="noopener noreferrer external">Jest</a>.</p>
<p>When I was replicating this test for the purpose of this blog post, I figured out that I was actually using <a href="https://jasmine.github.io" rel="noopener noreferrer external">Jasmine</a> as it is the default test suite used when creating new <a href="https://ionicframework.com/docs/angular/your-first-app" rel="noopener noreferrer external">Ionic Angular</a> applications 😁.</p>
<p>That’s why I am sharing today both solutions or how to mock a private function with Jasmine or Jest 😇.</p>
<hr />
<h3>Credits</h3>
<p>This blog post Jest’s solution has been provided by <a href="https://stackoverflow.com/users/10149510/brian-adams" rel="noopener noreferrer external">Brian Adams</a> on <a href="https://stackoverflow.com/questions/56044471/testing-private-functions-in-typescript-with-jest/56045577#56045577" rel="noopener noreferrer external">Stackoverflow</a>. The Jasmine one was inspired by the answer of <a href="https://stackoverflow.com/users/4936193/jurl" rel="noopener noreferrer external">jurl</a> on the same <a href="https://stackoverflow.com/questions/56044471/testing-private-functions-in-typescript-with-jest/56045577#56045577" rel="noopener noreferrer external">platform</a> too.</p>
<p>Kudos to them, not all heroes wear capes!</p>
<hr />
<h3>Test Setup</h3>
<p>Once again, I am using my favorite API for the demo purpose: the free <a href="https://dog.ceo/dog-api/" rel="noopener noreferrer external">DOG Api</a>.</p>
<p>Let’s then agree that our goal is to create a test for the following service which does not do much beside fetching a random dog but note that I explicitly marked the query function as a <code>private</code> method for demonstration purpose.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Injectable} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {HttpClient} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/common/http</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">interface</span><span style="color:#8BE9FD;font-style:italic"> DogResponse</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    message</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    status</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Injectable</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    providedIn</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">root</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> DoggosService</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    constructor</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">private</span><span style="color:#FFB86C;font-style:italic"> httpClient</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HttpClient</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    async</span><span style="color:#50FA7B"> findDoggo</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> response</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> DogResponse</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">searchDoggos</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">response) {</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#BD93F9"> null</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> response.message;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    private</span><span style="color:#50FA7B"> searchDoggos</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">DogResponse</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> url </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">https://dog.ceo/api/breeds/image/random</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.httpClient.</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">DogResponse</span><span style="color:#F8F8F2">>(url).</span><span style="color:#50FA7B">toPromise</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>Failing Unit Test</h3>
<p>Before trying to mock our private function, I thought that writing a test which fails would be a good start.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {TestBed} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core/testing</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {HttpClientTestingModule, HttpTestingController} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/common/http/testing</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {DoggosService} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./doggos.service</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">describe</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">DoggosService</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> httpTestingController</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HttpTestingController</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> service</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> DoggosService</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    beforeEach</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        TestBed.</span><span style="color:#50FA7B">configureTestingModule</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">            imports</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [HttpClientTestingModule]</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        httpTestingController </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> TestBed.</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(HttpTestingController);</span></span>
<span class="line"><span style="color:#F8F8F2">        service </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> TestBed.</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(DoggosService);</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    it</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">should be created</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">        expect</span><span style="color:#F8F8F2">(service).</span><span style="color:#50FA7B">toBeTruthy</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    it</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">should fetch a doggo</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, </span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> mockUrl </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">https://images.dog.ceo/breeds/setter-irish/n02100877_1965.jpg</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> data</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> service.</span><span style="color:#50FA7B">findDoggo</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">        expect</span><span style="color:#F8F8F2">(data).not.</span><span style="color:#50FA7B">toBe</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">null</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#50FA7B">        expect</span><span style="color:#F8F8F2">(data).</span><span style="color:#50FA7B">toEqual</span><span style="color:#F8F8F2">(mockUrl);</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><p>Because we are performing an HTTP request and are not mocking it, the test fails on a timeout. That’s why our goal will be to solve this issue while mocking the <code>private</code> function which takes care of performing the request.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*d2u_KJrrxiKqi-HKgsTfFw.png" alt="" / loading="lazy"></p>
<hr />
<h3>Mock A Private Function With Jasmine</h3>
<p>To mock a private function with Jasmine, we can spy on our service private function <code>searchDoggos</code> and use a fake callback, <code>callFake</code> , to provide the mocked data as return when needed. Moreover, we can also test that our function has effectively been executed.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">it</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">should fetch a doggo</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, </span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> mockUrl </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#E9F284">    '</span><span style="color:#F1FA8C">https://images.dog.ceo/breeds/setter-irish/n02100877_1965.jpg</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> handleSpy </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> spyOn</span><span style="color:#F8F8F2">(</span><span style="color:#8BE9FD;font-style:italic">DoggosService</span><span style="color:#F8F8F2">.prototype </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> any</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">                            '</span><span style="color:#F1FA8C">searchDoggos</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    handleSpy.and.</span><span style="color:#50FA7B">callFake</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">            resolve</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">                message</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> mockUrl,</span></span>
<span class="line"><span style="color:#F8F8F2">                status</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">success</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">            });</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> data</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> service.</span><span style="color:#50FA7B">findDoggo</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    expect</span><span style="color:#F8F8F2">(data).not.</span><span style="color:#50FA7B">toBe</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">null</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#50FA7B">    expect</span><span style="color:#F8F8F2">(data).</span><span style="color:#50FA7B">toEqual</span><span style="color:#F8F8F2">(mockUrl);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    expect</span><span style="color:#F8F8F2">(handleSpy).</span><span style="color:#50FA7B">toHaveBeenCalled</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><p>Thanks to these changes, we are now able to run our test with success 🥳.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*072z86Cn9K5wz_1U4d0D9Q.png" alt="" / loading="lazy"></p>
<hr />
<h3>Mock A Private Function With Jest</h3>
<p>The Jest solution follow the same logic as the above one except that we take advantages of the <code>mockImplementation</code> method to mock the private function.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">it</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">should fetch a doggo</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, </span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> mockUrl </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#E9F284">    '</span><span style="color:#F1FA8C">https://images.dog.ceo/breeds/setter-irish/n02100877_1965.jpg</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> handleSpy </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> jest.</span><span style="color:#50FA7B">spyOn</span><span style="color:#F8F8F2">(</span><span style="color:#8BE9FD;font-style:italic">DoggosService</span><span style="color:#F8F8F2">.prototype </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> any</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">                                 '</span><span style="color:#F1FA8C">searchDoggos</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    handleSpy.</span><span style="color:#50FA7B">mockImplementation</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#FF79C6"> =></span></span>
<span class="line"><span style="color:#50FA7B">            resolve</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">                message</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> mockUrl,</span></span>
<span class="line"><span style="color:#F8F8F2">                status</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">success</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">            })</span></span>
<span class="line"><span style="color:#F8F8F2">        );</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> data</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> service.</span><span style="color:#50FA7B">findDoggo</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    expect</span><span style="color:#F8F8F2">(data).not.</span><span style="color:#50FA7B">toBe</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">null</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#50FA7B">    expect</span><span style="color:#F8F8F2">(data).</span><span style="color:#50FA7B">toEqual</span><span style="color:#F8F8F2">(mockUrl);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    expect</span><span style="color:#F8F8F2">(handleSpy).</span><span style="color:#50FA7B">toHaveBeenCalled</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><hr />
<h3>Summary</h3>
<p>Even though it looks really trivial once summarized, it took me a bit of time to find these solutions and I am really grateful that both Brian and jurl posted their answers on Stackoverflow. Hopefully this might help someone some day too!</p>
<p>Stay home, stay safe!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[React And Web Workers]]></title>
          <description><![CDATA[How to interact with Web Workers in React applications]]></description>
          <link>https://daviddalbusco.com/blog/react-and-web-workers/</link>
          <pubDate>Tue, 07 Apr 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*zuX14URmAiO36mikljilOg.png" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@tobiastu?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Tobias Tullius</a> on <a href="https://unsplash.com/s/photos/free?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I share <a href="https://daviddalbusco.com/blog/how-to-call-the-service-worker-from-the-web-app-context" rel="noopener noreferrer external">one trick a day</a> until (probably not) the end of the COVID-19 quarantine in Switzerland, April 19th 2020. <strong>Twelve</strong> days left until hopefully better days.</p>
<hr />
<p>I recently published <a href="https://tietracker.app.link" rel="noopener noreferrer external">Tie Tracker</a>, a simple, open source and free time tracking app ⏱.</p>
<p>Among its features, the full offline mode was particularly interesting to develop. From an architectural point of view, I had to find a solution to compute, for statistical or exportation purposes, the many entries the users are potentially able to record without blocking the user interface.</p>
<p>That’s why I had the idea to solve my problem with the help of the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers" rel="noopener noreferrer external">Web Workers API</a>.</p>
<p>The app is developed with <a href="https://ionicframework.com" rel="noopener noreferrer external">Ionic</a> + <a href="https://reactjs.org" rel="noopener noreferrer external">React</a>, therefore let me share with you my recipe 😉.</p>
<hr />
<h3>Simulate A Blocked User Interface</h3>
<p>Before trying Web Workers out, let’s first try to develop a small application which contains an action which actually block the user interface.</p>
<p>In the following component, we are handling two states, two counters. One of these is incremented on each button click while the other call a function <code>incApple()</code> which loops for a while and therefore block the user interaction.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    IonContent,</span></span>
<span class="line"><span style="color:#F8F8F2">    IonPage,</span></span>
<span class="line"><span style="color:#F8F8F2">    IonLabel,</span></span>
<span class="line"><span style="color:#F8F8F2">    IonButton</span></span>
<span class="line"><span style="color:#F8F8F2">} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@ionic/react</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> React, {useState} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {RouteComponentProps} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">react-router</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./Page.css</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> Page</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> React</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">FC</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">RouteComponentProps</span><span style="color:#F8F8F2">&#x3C;{ name</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">; }>> </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> ({</span><span style="color:#FFB86C;font-style:italic">match</span><span style="color:#F8F8F2">}) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> [countTomato, setCountTomato] </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useState</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">number</span><span style="color:#F8F8F2">>(</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> [countApple, setCountApple] </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useState</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">number</span><span style="color:#F8F8F2">>(</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    function</span><span style="color:#50FA7B"> incApple</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> start </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> Date.</span><span style="color:#50FA7B">now</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">        while</span><span style="color:#F8F8F2"> (Date.</span><span style="color:#50FA7B">now</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2"> start </span><span style="color:#FF79C6">+</span><span style="color:#BD93F9"> 5000</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#50FA7B">        setCountApple</span><span style="color:#F8F8F2">(countApple </span><span style="color:#FF79C6">+</span><span style="color:#BD93F9"> 1</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonPage</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonContent</span><span style="color:#50FA7B;font-style:italic"> className</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ion-padding</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonLabel</span><span style="color:#F8F8F2">>Tomato: </span><span style="color:#FF79C6">{</span><span style="color:#F8F8F2">countTomato</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2"> | Apple: </span><span style="color:#FF79C6">{</span><span style="color:#F8F8F2">countApple</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonLabel</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> className</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ion-padding-top</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonButton</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                     onClick</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> setCountTomato</span><span style="color:#F8F8F2">(countTomato </span><span style="color:#FF79C6">+</span><span style="color:#BD93F9"> 1</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                     color</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">primary</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Tomato&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonButton</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonButton</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                     onClick</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> incApple</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                     color</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">secondary</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Apple&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonButton</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonContent</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonPage</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> Page;</span></span></code></pre><p>As you can notice in the following animated Gif, as soon as I start the “Apple counter”, the user interaction on the “Tomato counter” have no effects anymore, do not trigger any new component rendering, as the function is currently blocking the JavaScript thread.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*UFSpBIR1gR1etkBkLdXRaA.gif" alt="" / loading="lazy"></p>
<hr />
<h3>Defer Work With Web Workers</h3>
<p>Having the above example in mind, let’s try out Web Workers in order to defer our “Apple counter” function.</p>
<hr />
<h4>Web Workers</h4>
<p>To easiest way to add a Web Worker to your application is to ship it as an asset. In the case of my Ionic React application, these find place in the directory <code>public</code> , that’s we create a new file <code>./public/workers/apple.js</code> .</p>
<p>Before explaining the flow of the following code, two things are <strong>important</strong> to notice:</p>
<ol>
<li><p>The application and the Web Workers are two separate things. They don’t share states, they don’t share libraries, they are separate and can communicate between them through messages only.</p></li>
<li><p>Web Workers do not have access to the GUI, to the <code>document</code>, to the <code>window</code>.</p></li>
</ol>
<p>If you are familiar with Firebase, you can kind of understand, to some extent, the Web Worker as your own private, not Cloud, but local functions.</p>
<p>The entry point of our web worker is <code>onmessage</code> which is basically a listener to call triggered from our application. In the function we are registering, we are checking if a corresponding <code>msg</code> is provided, this let us use a web worker for many purposes, and are also amending the current counter value before running the same function <code>incApple()</code> as before. Finally, instead of updating the state directly, we are returning the value to the application through a <code>postMessage</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">self.</span><span style="color:#50FA7B">onmessage</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">$event</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> ($event </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> $event.data </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> $event.data.msg </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">incApple</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> newCounter </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> incApple</span><span style="color:#F8F8F2">($event.data.countApple);</span></span>
<span class="line"><span style="color:#F8F8F2">        self.</span><span style="color:#50FA7B">postMessage</span><span style="color:#F8F8F2">(newCounter);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">function</span><span style="color:#50FA7B"> incApple</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">countApple</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> start </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> Date.</span><span style="color:#50FA7B">now</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">    while</span><span style="color:#F8F8F2"> (Date.</span><span style="color:#50FA7B">now</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2"> start </span><span style="color:#FF79C6">+</span><span style="color:#BD93F9"> 5000</span><span style="color:#F8F8F2">) {}</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> countApple </span><span style="color:#FF79C6">+</span><span style="color:#BD93F9"> 1</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h4>Interacting With The Web Workers</h4>
<p>To interact with the web worker, we first need to add a reference point to our component.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> appleWorker</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Worker</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Worker</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">./workers/apple.js</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span></code></pre><p>Because we are communicating with the use of messages, we should then register a listener which would take care of updating the counter state when the web worker emits a result.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">useEffect</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    appleWorker.</span><span style="color:#50FA7B">onmessage</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">$event</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> MessageEvent</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> ($event </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> $event.data) {</span></span>
<span class="line"><span style="color:#50FA7B">            setCountApple</span><span style="color:#F8F8F2">($event.data);</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"><span style="color:#F8F8F2">}, [appleWorker]);</span></span></code></pre><p>Finally we update our function <code>incApple()</code> to call the web worker.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">function</span><span style="color:#50FA7B"> incApple</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#F8F8F2">    appleWorker.</span><span style="color:#50FA7B">postMessage</span><span style="color:#F8F8F2">({ msg</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">incApple</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, countApple</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> countApple });</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Tada, that’s it 🎉. You should now be able to interact with the GUI even if the “blocker code is running”. As you can notice in the following animated Gif, I am still able to increment my tomato counter even if the blocking loops is performed by the web worker.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*ckzRpU3hVOmPyHw-of1u2w.gif" alt="" / loading="lazy"></p>
<p>The component altogether in case you would need it:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    IonContent,</span></span>
<span class="line"><span style="color:#F8F8F2">    IonPage,</span></span>
<span class="line"><span style="color:#F8F8F2">    IonLabel,</span></span>
<span class="line"><span style="color:#F8F8F2">    IonButton</span></span>
<span class="line"><span style="color:#F8F8F2">} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@ionic/react</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> React, {useEffect, useState} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {RouteComponentProps} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">react-router</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./Page.css</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> Page</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> React</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">FC</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">RouteComponentProps</span><span style="color:#F8F8F2">&#x3C;{ name</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">; }>> </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> ({</span><span style="color:#FFB86C;font-style:italic">match</span><span style="color:#F8F8F2">}) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> [countTomato, setCountTomato] </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useState</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">number</span><span style="color:#F8F8F2">>(</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> [countApple, setCountApple] </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useState</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">number</span><span style="color:#F8F8F2">>(</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> appleWorker</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Worker</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Worker</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">./workers/apple.js</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    useEffect</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        appleWorker.</span><span style="color:#50FA7B">onmessage</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">$event</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> MessageEvent</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">            if</span><span style="color:#F8F8F2"> ($event </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> $event.data) {</span></span>
<span class="line"><span style="color:#50FA7B">                setCountApple</span><span style="color:#F8F8F2">($event.data);</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"><span style="color:#F8F8F2">        };</span></span>
<span class="line"><span style="color:#F8F8F2">    }, [appleWorker]);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    function</span><span style="color:#50FA7B"> incApple</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#F8F8F2">        appleWorker</span></span>
<span class="line"><span style="color:#F8F8F2">            .</span><span style="color:#50FA7B">postMessage</span><span style="color:#F8F8F2">({msg</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">incApple</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, countApple</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> countApple});</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonPage</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonContent</span><span style="color:#50FA7B;font-style:italic"> className</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ion-padding</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonLabel</span><span style="color:#F8F8F2">>Tomato: </span><span style="color:#FF79C6">{</span><span style="color:#F8F8F2">countTomato</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2"> | Apple: </span><span style="color:#FF79C6">{</span><span style="color:#F8F8F2">countApple</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonLabel</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> className</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ion-padding-top</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonButton</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                     onClick</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> setCountTomato</span><span style="color:#F8F8F2">(countTomato </span><span style="color:#FF79C6">+</span><span style="color:#BD93F9"> 1</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                     color</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">primary</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Tomato&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonButton</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonButton</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                     onClick</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> incApple</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                     color</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">secondary</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Apple&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonButton</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonContent</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonPage</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> Page;</span></span></code></pre><hr />
<h3>Summary</h3>
<p>Web Workers is really an interesting concept. <a href="https://tietracker.app.link" rel="noopener noreferrer external">Tie Tracker</a> let me experiment them and I am definitely going to use them again in future projects. Its code is open source and available on <a href="https://github.com/peterpeterparker/tietracker" rel="noopener noreferrer external">GitHub</a>. If you have any feedback and even better, are interested to contribute, send me your best Pull Requests, that would be awesome 😎.</p>
<p>Stay home, stay safe!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Third Party Service Providers. Be transparent to each other!]]></title>
          <description><![CDATA[Call to display your third party providers.]]></description>
          <link>https://daviddalbusco.com/blog/third-party-service-providers-be-transparent-to-each-other/</link>
          <pubDate>Mon, 06 Apr 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*5qdXAhgCLQUv5yxL_wiDaQ.png" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@elijahsad?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Elijah O’Donnell</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I share <a href="https://daviddalbusco.com/blog/how-to-call-the-service-worker-from-the-web-app-context" rel="noopener noreferrer external">one trick a day</a> until (probably not) the end of the COVID-19 quarantine in Switzerland, April 19th 2020. <strong>Thirteen</strong> days left until hopefully better days.</p>
<hr />
<p>I don’t know if you share my observation but I feel like currently we have reached a point in the Coronavirus crisis where every third news is either about another <a href="https://techcrunch.com/2020/04/01/daily-crunch-zoom-faces-security-scrutiny/" rel="noopener noreferrer external">Zoom data breach</a> or about a new solution or application which aims to solve and improve the current situation with the help of “anonymized cell phone geolocation data” or other behavioral data.</p>
<p>From a personal point of view, moreover than making me aware than once again I should pay attention to these subjects, these news also reminded me something small we had put in place in <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>, our open source editor for presentations..</p>
<p>Indeed, like most of the web platforms, we do have a <a href="https://deckdeckgo.com/privacy" rel="noopener noreferrer external">privacy policy</a> and <a href="https://deckdeckgo.com/terms" rel="noopener noreferrer external">terms of services</a>, like some we are also open source and all our code is available publicly on <a href="https://github.com/deckgo/deckdeckgo" rel="noopener noreferrer external">GitHub</a> but what we do have and, to my knowledge few also have, is a <a href="https://deckdeckgo.com/services" rel="noopener noreferrer external">dedicated page</a> where we summarize transparently all the third party service providers we are using to provide our solution.</p>
<p>It is maybe not much, but such a page is to our eyes really worthy. No long pages to scroll, no hidden information, no tricks, no b*ullshit (excuse my french), just one page in which you can find quickly what services and what partners are used, period.</p>
<p>It might not speak to everybody, I'm agree, and I don't except neither big companies to do the same as well, but if just one person would read this article, maybe implement it too or improve it with any feedback, I think that would be already a small step towards a more transparent web and that's why I am sharing the idea with you today.</p>
<hr />
<p>Here is the copy of what we are currently using respectively what we are displaying in such a page (for later reader, today is the 6th April 2020):</p>
<h3>Services</h3>
<p>We aim to be transparent, therefore, furthermore than open sourcing our all code on <a href="https://github.com/deckgo/deckdeckgo" rel="noopener noreferrer external">GitHub</a>, here are the list of services we are using to provide DeckDeckGo.</p>
<h4>Amazon</h4>
<p>We use AWS <a href="https://aws.amazon.com/lambda/" rel="noopener noreferrer external">Lambda</a>, <a href="https://aws.amazon.com/rds/" rel="noopener noreferrer external">RDS</a>, <a href="https://aws.amazon.com/s3/" rel="noopener noreferrer external">S3</a> and <a href="https://aws.amazon.com/sqs/" rel="noopener noreferrer external">SQS</a> to save and publish online the presentations as Progressive Web Apps. The choice behind this is mostly the fact that we thought that the S3 solution was a good one for our purpose but beside that, it was also challenging to run Haskell Webapps on AWS Lambda.</p>
<h4>Google</h4>
<p>We are using <a href="https://firebase.google.com/products/firestore/" rel="noopener noreferrer external">Firestore</a> to save your data and the presentations you are editing. We are also using Google Firebase <a href="https://firebase.google.com/products/hosting/" rel="noopener noreferrer external">Hosting</a> and <a href="https://firebase.google.com/products/auth/" rel="noopener noreferrer external">Authentication</a>. Both feature are good match to serve and deploy easily Progressive Web Apps. Their Authentication is also interesting as it provides the social login we were looking for (like Email and GitHub).</p>
<h4>Tenor and Unsplash</h4>
<p>To provide a user friendly gifs and stock photos integration we have integrated the APIs provided by <a href="https://tenor.com/" rel="noopener noreferrer external">Tenor</a>, which is owned by Google, and <a href="https://unsplash.com/" rel="noopener noreferrer external">Unsplash</a>.</p>
<h4>Font Awesome</h4>
<p>The shapes, which could be integrated in your presentation, are free icons provided by <a href="https://fontawesome.com/" rel="noopener noreferrer external">Font Awesome</a>. We do not use any APIs to fetch these respectively we are hosting them.</p>
<h4>Mailchimp</h4>
<p>In order to send time to time newsletters, mostly when we are releasing new features, we are using <a href="https://mailchimp.com/" rel="noopener noreferrer external">Mailchimp</a>. Upon creating an account users are opted into it but they can opt out through their account’s “Settings” page and at the link of the footer in any of these non-administrativ emails.</p>
<p>All these services are covered in our <a href="https://deckdeckgo.com/privacy" rel="noopener noreferrer external">Privacy Policy</a> and <a href="https://deckdeckgo.com/terms" rel="noopener noreferrer external">Terms of Services</a>.</p>
<hr />
<h3>Summary</h3>
<p>Do not misunderstand me, we are using third party services, we always apply a “privacy per default” approach but we are far away of being perfect, but at least, we always try our best and we are transparent.</p>
<p>Stay home, stay safe</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Create Your Own NPM Cli]]></title>
          <description><![CDATA[How to create your own npm init command for your project]]></description>
          <link>https://daviddalbusco.com/blog/create-your-own-npm-cli/</link>
          <pubDate>Sun, 05 Apr 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*QchuRONVGccKfb0imrxMig.png" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@smikefeola?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Michele Feola</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I share <a href="https://daviddalbusco.com/blog/how-to-call-the-service-worker-from-the-web-app-context" rel="noopener noreferrer external">one trick a day</a> until (probably not) the end of the COVID-19 quarantine in Switzerland, April 19th 2020. <strong>Fourteen</strong> days left until hopefully better days.</p>
<hr />
<p>If you rather like to develop your slides with our developer kit rather than with our <a href="https://deckdeckgo.com" rel="noopener noreferrer external">online editor</a> for presentations, there is a good change that you are going to begin your journey by running the command <code>npm init deckdeckgo</code> in your terminal.</p>
<p>In this article I share how to create a <a href="https://www.npmjs.com" rel="noopener noreferrer external">npm</a> CLI command for your project from scratch.</p>
<p>Note that this article as our Cli is more than inspired by the amazing <a href="https://github.com/ionic-team/create-stencil" rel="noopener noreferrer external">Stencil’s Cli</a>.</p>
<hr />
<h3>NPM Init</h3>
<p>Many web projects are offering a Cli to ease the creation of new projects. Using these, without any other prior installation rather than Node and npm, we are able to run command in our terminal to start new fresh project. As for example <code>npm init stencil</code> to create a component or app with <a href="https://stenciljs.com" rel="noopener noreferrer external">Stencil</a> or <code>npm init react-app</code> to start a new <a href="https://reactjs.org" rel="noopener noreferrer external">React</a> application.</p>
<p>This is possible thanks to npm and their <a href="https://docs.npmjs.com/cli/init.html" rel="noopener noreferrer external">Cli commands</a> support.</p>
<p>In order to create such tool, we have to create and publish a project, our Cli itself, which contains an <code>index.js</code> and execute a main function when called. But more important, what’s really crucial, is the naming of the project. Indeed it has to be prefixed with <code>create-</code> in order to be later on resolved by your command line.</p>
<p>For example, our project’s name is <code>DeckDeckGo</code>, therefore the related Cli project’s name is <code>create-deckdeckgo</code> . Doing so, each time someone runs <code>npm init deckdeckgo</code> in his/her terminal, npm performs a lookup for a related <code>create-</code> project and if found, download it locally and runs the main function.</p>
<hr />
<h3>Create A New Cli Project</h3>
<p>Let’s try to create our own CLI called “Hello”.</p>
<p>As explained above, the project’s Cli name has to be prefixed with <code>create-</code> that’s why we create a new folder <code>create-hello</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">mkdir</span><span style="color:#F1FA8C"> create-hello</span><span style="color:#F8F8F2"> &#x26;&#x26; </span><span style="color:#8BE9FD">cd</span><span style="color:#F1FA8C"> create-hello</span></span></code></pre><p>We then define a <code>package.json</code> which, furthermore than defining the entry point <code>index.js</code> for the <code>bin</code>, set up the scripts and dependencies in order be able to develop and build our project with <a href="https://rollupjs.org" rel="noopener noreferrer external">Rollup</a> and <a href="https://www.typescriptlang.org" rel="noopener noreferrer external">TypeScript</a>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">name</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">create-hello</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">version</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">1.0.0</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">main</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">dist/index.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">scripts</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">start</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">node dist/index.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">build.tsc</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">tsc</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">build.bundle</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">rollup -c</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">minify</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">terser --compress --mangle --toplevel --output dist/index.js -- dist/index.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">build</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">npm run build.tsc &#x26;&#x26; npm run build.bundle &#x26;&#x26; npm run minify</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">build.dev</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">npm run build.tsc &#x26;&#x26; npm run build.bundle</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">dev</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">npm run build.dev &#x26;&#x26; npm start</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">version</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">npm build</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    },</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">files</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">dist/index.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">bin</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">create-hello</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">dist/index.js</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    },</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">devDependencies</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">rollup</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">^2.3.3</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">rollup-plugin-commonjs</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">^10.1.0</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">rollup-plugin-json</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">^4.0.0</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">rollup-plugin-node-resolve</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">^5.2.0</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">terser</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">^4.6.10</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">tslint</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">^6.1.1</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">tslint-ionic-rules</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">0.0.21</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">typescript</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">^3.8.3</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    },</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">dependencies</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {}</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Using TypeScript means defining a <code>tsconfig.json</code> :</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">compilerOptions</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">moduleResolution</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">node</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">target</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">es2015</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">allowJs</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">module</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">es2015</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">lib</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">es2015</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">strict</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">noEmitOnError</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">sourceMap</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">declaration</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">allowSyntheticDefaultImports</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">experimentalDecorators</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">emitDecoratorMetadata</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">outDir</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">dist/src</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">strictNullChecks</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> false</span></span>
<span class="line"><span style="color:#F8F8F2">    },</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">files</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">src/index.ts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">]</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>And some linter rules:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">extends</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">tslint-ionic-rules</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">rules</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">no-conditional-assignment</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">no-non-null-assertion</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">no-unnecessary-type-assertion</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">prefer-for-of</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">no-import-side-effect</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">ordered-imports</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#BD93F9">            true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">            {</span></span>
<span class="line"><span style="color:#8BE9FE">                "</span><span style="color:#8BE9FD">named-imports-order</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">lowercase-last</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"><span style="color:#F8F8F2">        ]</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Finally we also need to setup our Rollup build, notably in order to be able to run commands which interact with the file system. Not the goal from this article but might be useful if we would like to create a real Cli which has for goal to create new fresh local projects.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> resolve </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">rollup-plugin-node-resolve</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> commonjs </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">rollup-plugin-commonjs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> json </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">rollup-plugin-json</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    input</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">dist/src/index.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    output</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        file</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">dist/index.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        format</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">cjs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        strict</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        banner</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">#! /usr/bin/env node</span><span style="color:#FF79C6">\n</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    },</span></span>
<span class="line"><span style="color:#F8F8F2">    plugins</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#50FA7B">resolve</span><span style="color:#F8F8F2">(), </span><span style="color:#50FA7B">json</span><span style="color:#F8F8F2">(), </span><span style="color:#50FA7B">commonjs</span><span style="color:#F8F8F2">({ include</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">node_modules/**</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> })],</span></span>
<span class="line"><span style="color:#F8F8F2">    external</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#E9F284">        "</span><span style="color:#F1FA8C">child_process</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">        "</span><span style="color:#F1FA8C">fs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">        "</span><span style="color:#F1FA8C">path</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">        "</span><span style="color:#F1FA8C">os</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">        "</span><span style="color:#F1FA8C">https</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">        "</span><span style="color:#F1FA8C">readline</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">        "</span><span style="color:#F1FA8C">zlib</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">        "</span><span style="color:#F1FA8C">events</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">        "</span><span style="color:#F1FA8C">stream</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">        "</span><span style="color:#F1FA8C">util</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">        "</span><span style="color:#F1FA8C">buffer</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    ]</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><hr />
<h3>Code Your Cli</h3>
<p>Everything is in place we can now develop our Cli. As staten above, nothing more than an <code>index</code> entry with a main function, therefore, let’s create a new file <code>src/index.ts</code> which does nothing more than printing out “Hello World”.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> run</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Hello World</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">run</span><span style="color:#F8F8F2">();</span></span></code></pre><p>Once the dependencies ( <code>npm install</code> ) installed, we should be able to build and run the project.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> run</span><span style="color:#F1FA8C"> build</span><span style="color:#F8F8F2"> &#x26;&#x26; </span><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> run</span><span style="color:#F1FA8C"> start</span></span></code></pre><p>If everything works as expected, you should notice a “Hello World” printed out in your terminal.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*YvNBHjvEjfv0dzCuZKE_Cw.png" alt="" / loading="lazy"></p>
<hr />
<h3>Publish Your Cli</h3>
<p>Even if it does nothing much yet, we are actually already able to publish our Cli to npm ( <code>npm publish</code> ). If we would do so and once successfully published, everyone everywhere running <code>npm init hello</code> would then be able to print out “Hello World” in his/her terminal 😉.</p>
<hr />
<h3>Going Further</h3>
<p>Here’s a couple of things you might found interesting if you plan to develop your own Cli “for real”.</p>
<hr />
<h4>Arguments</h4>
<p>We might want to listen to some arguments ( <code>args</code> ). Commonly, we might be looking to print out some information if the user pass the arguments <code>--help</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">function</span><span style="color:#50FA7B"> run</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> args </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> process.argv.</span><span style="color:#50FA7B">slice</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">2</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> help </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> args.</span><span style="color:#50FA7B">indexOf</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">--help</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">>=</span><span style="color:#BD93F9"> 0</span><span style="color:#FF79C6"> ||</span><span style="color:#F8F8F2"> args.</span><span style="color:#50FA7B">indexOf</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">-h</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">>=</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (help) {</span></span>
<span class="line"><span style="color:#F8F8F2">        console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Run your command without arguments.</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Hello World</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">run</span><span style="color:#F8F8F2">();</span></span></code></pre><p>We can test the above while running the command line <code>npm run build &amp;&amp; npm run start -- --help</code> . Note that the double <code>--</code> are only needed as we are trying out locally our bundle.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*pW72fAU534R3ujl3fusJfg.png" alt="" / loading="lazy"></p>
<hr />
<h4>Colors</h4>
<p>Life without colors is sad 😥. Let’s use <a href="https://github.com/jorgebucaran/colorette" rel="noopener noreferrer external">Colorette</a> ( <code>npm install colorette --save</code> ) to brighten our “Hello World”.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { magenta } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">colorette</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">function</span><span style="color:#50FA7B"> run</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">magenta</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Hello World</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">));</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">run</span><span style="color:#F8F8F2">();</span></span></code></pre><p>Have a look to this beautiful magenta color, isn’t that more user friendly happy?</p>
<p><img src="https://daviddalbusco.com/assets/images/1*C1WKqoLBbBty0UbOvf7vqQ.png" alt="" / loading="lazy"></p>
<hr />
<h4>Interactive Command Line</h4>
<p>Moreover than arguments, we might want to ask the user some questions or give him/her some options while executing our Cli. For that purpose I like to use <a href="https://github.com/SBoudrias/Inquirer.js/" rel="noopener noreferrer external">inquirer</a> ( <code>npm install inquirer --save</code> and <code>npm install @types/inquirer --save-dev</code> ).</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {cyan, magenta} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">colorette</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">function</span><span style="color:#50FA7B"> run</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">magenta</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Hello World</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">));</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> inquirerHappy </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> require</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">inquirer</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> questionHappy </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#F8F8F2">        {</span></span>
<span class="line"><span style="color:#F8F8F2">            type</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">confirm</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">            name</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">happy</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">            message</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">Are you happy today?</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">            default</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    ];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> answer </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> inquirerHappy.</span><span style="color:#50FA7B">prompt</span><span style="color:#F8F8F2">(questionHappy);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">cyan</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#F1FA8C">           `You are</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">answer.happy</span><span style="color:#FF79C6"> ?</span><span style="color:#E9F284"> ''</span><span style="color:#FF79C6"> :</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C"> not</span><span style="color:#E9F284">'</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C"> happy today.`</span><span style="color:#F8F8F2">));</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">run</span><span style="color:#F8F8F2">();</span></span></code></pre><p>Obviously if I run the above I will answer yes 😁.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*1YsJnUuPSaca7PFp3wHhlQ.png" alt="" / loading="lazy"></p>
<hr />
<h4>Effectively Creating A Project</h4>
<p>If you are looking to create your own Cli there is a chance that your goal is to create new projects locally, that’s why I will just point out my approach of the problem. To me the Cli can solve this with the following steps:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">function</span><span style="color:#50FA7B"> createProject</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#50FA7B">    downloadFromGitHub</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    installDependencies</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    updateLocalValues</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>One, it fetches a project from GitHub or other repo. It can be a <code>git clone</code> or a <code>cURL</code> or any other commands. The idea is to get the source from a repo and to create a copy locally.</p>
<p>Then, if the project is a npm one, we might want to install the dependencies. Not that in such case it is important to trace the commands and to be sure to perform a <code>cleanUp</code> at the end of the all process.</p>
<p>Finally, as we copy or clone a projects, we might want to update some variables we might have asked before. Typically, with our Cli, we ask you what’s the name of your presentation, or what’s your name? These information are replaced automatically.</p>
<p>To process all these steps, checkout or clone our <a href="https://github.com/deckgo/deckdeckgo/tree/master/cli" rel="noopener noreferrer external">repo</a>.</p>
<hr />
<h3>Summary</h3>
<p>I find really cool, even if your project is a pet project like ours, to be able to create a Cli. Hopefully this blog post will help you create yours and if you have to prepare some slides, give a try to <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckgo</a> 👉 <code>npm init deckdeckgo</code> 🤗.</p>
<p>Stay home, stay safe</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[More JSX For Angular Developers]]></title>
          <description><![CDATA[A follow up introduction to JSX in Stencil or React for Angular developers]]></description>
          <link>https://daviddalbusco.com/blog/more-jsx-for-angular-developers/</link>
          <pubDate>Sat, 04 Apr 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*3SoWAzehzRTddHWjP-apUg.png" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@louieomelet?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Luyi Yang</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I share <a href="https://daviddalbusco.com/blog/how-to-call-the-service-worker-from-the-web-app-context" rel="noopener noreferrer external">one trick a day</a> until the end of the COVID-19 quarantine in Switzerland, April 19th 2020. <strong>Fifteen</strong> days left until hopefully better days.</p>
<hr />
<p>It is Saturday, I cleaned my flat, I could begin the writing of my today’s challenge blog post 😉.</p>
<p>Yesterday I published a brief introduction to <a href="https://daviddalbusco.com/blog/jjsx-for-angular-developers" rel="noopener noreferrer external">JSX for Angular Developers</a>. When I was about to finish writing, I noticed that there were still some materials which can be presented, that’s why I follow up with some more information regarding <a href="https://reactjs.org/docs/glossary.html#jsx" rel="noopener noreferrer external">JSX</a>, the syntax extension to JavaScript.</p>
<hr />
<h3>Data Binding</h3>
<p>I should have mentioned it in my yesterday’s post, but as <a href="https://angular.io" rel="noopener noreferrer external">Angular</a> use its own <a href="https://angular.io/guide/template-syntax" rel="noopener noreferrer external">templating syntax</a>, the one from JSX differs a bit.</p>
<p>Commonly you use double braces <code>{{}}</code> to bind values or brackets <code>[]</code> to set for examples conditional classes or pass variables to a component.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> *ngIf</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">odd</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> [class.danger]</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">count > 1</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>{{count}}&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">app-my-component</span><span style="color:#50FA7B;font-style:italic"> count</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">{{count}}</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">app-my-component</span><span style="color:#F8F8F2">></span></span></code></pre><p>Meanwhile with JSX, you are going to use single braces <code>{}</code>. Furthermore we can also take advantages of the <code>render</code> or <code>return</code> functions to use, if we would like, temporary variables to make the code even a bit cleaner by avoiding some long inline expressions.</p>
<p>The above coded with <a href="https://stenciljs.com" rel="noopener noreferrer external">Stencil</a>:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> myClass </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.count </span><span style="color:#FF79C6">></span><span style="color:#BD93F9"> 1</span><span style="color:#FF79C6"> ?</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">danger</span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> :</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Host</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> class</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">myClass</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span><span style="color:#FF79C6">{</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.count</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">my-component</span><span style="color:#50FA7B;font-style:italic"> count</span><span style="color:#FF79C6">={</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.count</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">my-component</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">Host</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Funny thing, you will not use <code>class</code> to define such in <a href="https://reactjs.org" rel="noopener noreferrer external">React</a> but rather <code>className</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;></span></span>
<span class="line"><span style="color:#FF79C6">        {</span><span style="color:#50FA7B">renderLabel</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">MyComponent</span><span style="color:#50FA7B;font-style:italic"> count</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">props.count</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">MyComponent</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/></span></span>
<span class="line"><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">function</span><span style="color:#50FA7B"> renderLabel</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> myClass </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> props.count </span><span style="color:#FF79C6">></span><span style="color:#BD93F9"> 1</span><span style="color:#FF79C6"> ?</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">danger</span><span style="color:#E9F284">"</span><span style="color:#FF79C6"> :</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> className</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">myClass</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span><span style="color:#FF79C6">{</span><span style="color:#F8F8F2">props.count</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>Loops</h3>
<p>Is it <code>in</code> or <code>of</code> or <code>let</code> , what’s the proper syntax of <code>*ngFor</code>? It has always been for me one of these things for which I have to think twice before being sure, what’s the proper way of writing it 😅.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#50FA7B;font-style:italic"> *ngFor</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">let value of values;</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>{{value}}&#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span></code></pre><p>While with JSX, there is not particular templating structure to remember, but rather how to use the array <code>map()</code> function.</p>
<p>Moreover, it is also worth to notice that if your list or collection aims to be dynamic, it is mandatory to add a <code>key</code> attribute to each entries to preserve the ordering.</p>
<p>Regardless if Stencil or React, beside the object reference <code>this</code> for Stencil, the code is the same.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.values.</span><span style="color:#50FA7B">map</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">entry</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">index</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#50FA7B;font-style:italic"> key</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">index</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span><span style="color:#FF79C6">{</span><span style="color:#F8F8F2">entry</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>Content Projection</h3>
<p>Even though it is maybe not something you probably use, or at least I use, every day, it is possible to pass content to an Angular component using <code>ng-content</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">ng-content</span><span style="color:#50FA7B;font-style:italic"> select</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">[start]</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">ng-content</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">ng-content</span><span style="color:#50FA7B;font-style:italic"> select</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">[end]</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">ng-content</span><span style="color:#F8F8F2">></span></span></code></pre><p>Doing so, the component becomes more reusable as it is possible to use it with some more flexibility regarding its content.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">app-my-component</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">h1</span><span style="color:#50FA7B;font-style:italic"> start</span><span style="color:#F8F8F2">>Hello&#x3C;/</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">h2</span><span style="color:#50FA7B;font-style:italic"> end</span><span style="color:#F8F8F2">>World&#x3C;/</span><span style="color:#FF79C6">h2</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">app-my-component</span><span style="color:#F8F8F2">></span></span></code></pre><p>To the contrary, content projection is something you may, or at least I may, use really often with Stencil because <code>slot</code> are a core features of the Web Components.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Host</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">slot</span><span style="color:#50FA7B;font-style:italic"> name</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">start</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">slot</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">slot</span><span style="color:#50FA7B;font-style:italic"> name</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">end</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">slot</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">Host</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>They can be use “anywhere”, with or without any modern frontend framework. That’s why they, Web Components, really shine to my eyes.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">my-component</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">h1</span><span style="color:#50FA7B;font-style:italic"> slot</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">start</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Hello&#x3C;/</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">h2</span><span style="color:#50FA7B;font-style:italic"> slot</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">end</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>World&#x3C;/</span><span style="color:#FF79C6">h2</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">my-component</span><span style="color:#F8F8F2">></span></span></code></pre><p>In React, a common way to solve composition is done through the use of the default <a href="https://reactjs.org/docs/composition-vs-inheritance.html" rel="noopener noreferrer external">special children</a> prop which would allow you to pass multiple children to a component.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> React, {ReactNode} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">interface</span><span style="color:#8BE9FD;font-style:italic"> MyProps</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    children</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> ReactNode</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> MyComponent</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> React</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">FC</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">MyProps</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">props</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> MyProps</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> &#x3C;></span></span>
<span class="line"><span style="color:#FF79C6">        {</span><span style="color:#F8F8F2">props.children</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#F8F8F2">     &#x3C;/>;</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> MyComponent;</span></span></code></pre><hr />
<h3>Events</h3>
<p>Finally, the Angular syntax expect parenthesis as declarations for events interactions.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">button</span><span style="color:#50FA7B;font-style:italic"> (click)</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">inc()</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Increment&#x3C;/</span><span style="color:#FF79C6">button</span><span style="color:#F8F8F2">></span></span></code></pre><p>JSX is to some extension closer to vanilla JavaScript as the events are prefixed with the <code>on</code> keyword followed by a function’s call. Note again, that beside <code>this</code> , the syntax is the same for both Stencil and React.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FF79C6">button</span><span style="color:#50FA7B;font-style:italic"> onClick</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">inc</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">>Increment&#x3C;/</span><span style="color:#FF79C6">button</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>Summary</h3>
<p>My two blog posts were really light introductions to JSX and I am pretty sure that you are eager to discover more technical details about it. That’s why I would say, give either Stencil or React a try, or even better, give them both a try as they both use the JSX syntax. It makes kind of accessible the switch from one to the other without too much pain.</p>
<p>Stay home, stay safe!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[JSX For Angular Developers]]></title>
          <description><![CDATA[A brief introduction to JSX in Stencil or React for Angular developers]]></description>
          <link>https://daviddalbusco.com/blog/jsx-for-angular-developers/</link>
          <pubDate>Fri, 03 Apr 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*GcEjTmqI8Rq_tPJGMywfnw.png" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@maelrenau?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Maël Renault</a> on <a href="https://unsplash.com/s/photos/free?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I share <a href="https://daviddalbusco.com/blog/how-to-call-the-service-worker-from-the-web-app-context" rel="noopener noreferrer external">one trick a day</a> until the end of the COVID-19 quarantine in Switzerland, April 19th 2020. <strong>Sixteen</strong> days left until hopefully better days.</p>
<hr />
<p>At first I wasn’t that much a fan of the <a href="https://www.typescriptlang.org/docs/handbook/jsx.html" rel="noopener noreferrer external">JSX</a> syntax when I discovered it while developing my first Web Components with <a href="https://stenciljs.com" rel="noopener noreferrer external">Stencil</a>. I was missing the <a href="https://angular.io" rel="noopener noreferrer external">Angular</a> HTML templates.</p>
<p>Nowadays? I might change my mind in the future again, but after having developed such an eco-system as <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a> and having even learned <a href="https://reactjs.org" rel="noopener noreferrer external">React</a>, I can definitely say that I actually feels all the contrary, I love JSX ❤️. Even probably more these days as I am developing Angular clients’ projects on a weekly basis.</p>
<p>That’s why I had this idea to write a really brief and I hope beginner friendly introduction to JSX as used in Stencil or React for Angular developers.</p>
<hr />
<h3>JSX vs HTML Templates</h3>
<p>If you write an Angular application, <strong>commonly</strong> you are going to separate your components in layers and even probably three separate files: the code (TypeScript), the style (CSS) and the template (HTML, the GUI).</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Component } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    selector</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">app-my-component</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    templateUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./my-component.component.html</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    styleUrls</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">./my-component.component.scss</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">]</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> MyComponentComponent</span><span style="color:#F8F8F2"> {}</span></span></code></pre><p>And the related template:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">>Hello, World!&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span></code></pre><p>With JSX, regardless if Stencil or React, you have this separation of concern too but you are not going to separate your template and code in two separate files. Everything is commonly packed in on file, even in a same <code>class</code> or <code>function</code> .</p>
<p>The separation of concern occurs on the code side. If you have a <code>class</code> , you will have to expose a method <code>render()</code> which returns what suppose to be, guess what, rendered. In short: “a method which renders your HTML code”.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Component, h } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@stencil/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    tag</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">my-component</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    styleUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">my-component.css</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> MyComponent</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">>Hello, World!&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>If you have a <code>function</code> , then instead of <code>render</code> you will have a <code>return</code> method which follows the same behavior.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> React </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> MyComponent</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> React</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">FC</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">>Hello, World!&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> MyComponent;</span></span></code></pre><p>Both Stencil and React do support <code>class</code> or <code>function</code> . These last type became or are becoming, I think, really popular in React thanks to the use and introduction of <code>Hooks</code> , which I am not going to cover in this article. If you are interested in a separate post about it, ping me! I still have many posts to write to fulfill my challenge 😆.</p>
<p>Note also that for the rest of this article, I will display the Stencil examples using <code>class</code> and the React one using <code>functions</code> .</p>
<hr />
<h3>Root Element</h3>
<p>One important difference is the notion of root element. In Angular, you don’t really care about if. If your template contains a single root element or multiple ones, it compiles in any case.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">>Hello, World!&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">>Salut&#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">>Hallo&#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span></code></pre><p>In JSX, to the contrary, it does matter. Your component should be developed to handle such cases.</p>
<p>Therefore, our first solution might be to group our children under a single HTML node.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Component, h } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@stencil/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    tag</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">my-component</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    styleUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">my-component.css</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> MyComponent</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">>Hello, World!&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">>Salut&#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">>Hallo&#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        );</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>That would work out but this would result on adding a not needed <code>div</code> tag, the parent one, to our DOM. That’s why both Stencil and React have their respective similar solution to this problem.</p>
<p>In Stencil you can use a <code>Host</code> element.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Component, h, Host } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@stencil/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    tag</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">my-component</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    styleUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">my-component.css</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> MyComponent</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Host</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">>Hello, World!&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">>Salut&#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">>Hallo&#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">Host</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        );</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>And in React you can use what is called a <a href="https://reactjs.org/docs/fragments.html" rel="noopener noreferrer external">Fragment</a>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> React </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> MyComponent</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> React</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">FC</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">>Hello, World!&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">>Salut&#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">>Hallo&#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/></span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> MyComponent;</span></span></code></pre><p>Finally, in Stencil, if you rather like not to use such container, you can return an <code>array</code> of elements. But I feel like, mostly for styling reason, that I used the above solution more often so far.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Component, h } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@stencil/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    tag</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">my-component</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    styleUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">my-component.css</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> MyComponent</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">>Hello, World!&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">>,</span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">>Salut&#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">>Hallo&#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        ];</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>States And Properties</h3>
<p>In Angular <code>public</code> variables are these used in the templates and for which any changes are triggering a new rendering (“the changes are applied to the GUI”).</p>
<p>Variables made <code>private</code> are these which are used internally in the component and for which, no new rendering is needed.</p>
<p>Moreover there is also the <a href="https://angular.io/api/core/Input" rel="noopener noreferrer external">Input</a> decorator which is used to expose a variable as property of the component.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Component, Input} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  selector</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-my-component</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  templateUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./my-component.component.html</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  styleUrls</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">./my-component.component.scss</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">]</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> MyComponentComponent</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  @</span><span style="color:#50FA7B">Input</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">  count </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  odd </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#F8F8F2"> even </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  inc</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#6272A4">    // Render again</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.count</span><span style="color:#FF79C6">++</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.odd </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.count </span><span style="color:#FF79C6">%</span><span style="color:#BD93F9"> 2</span><span style="color:#FF79C6"> ===</span><span style="color:#BD93F9"> 1</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // Do not trigger a new render</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.even </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.count </span><span style="color:#FF79C6">%</span><span style="color:#BD93F9"> 2</span><span style="color:#FF79C6"> ===</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>And corresponding template:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">>Hello, World!&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">>{{odd}} {{count}}&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span></code></pre><p>In JSX, you find the same approach but kind of split in two categories, <code>state</code> and <code>properties</code> , for which, any changes will trigger a new render of the component. On the other side, if you have a variable which is neither one of these, then no render will be triggered again.</p>
<p><code>properties</code> are kind of the corresponding idea to the <code>@Input()</code> fields, these are the exposed properties of the components.</p>
<p><code>states</code> are kind of Angular <code>public</code> variables which have not been marked as inputs.</p>
<p>Concretely in Stencil you use <code>decorator</code> for such purpose.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Component, h, Host, Prop, State} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@stencil/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  tag</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">my-component</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  styleUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">my-component.css</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> MyComponent</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  @</span><span style="color:#50FA7B">Prop</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">  count </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  @</span><span style="color:#50FA7B">State</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#F8F8F2"> odd </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  even </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  inc</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#6272A4">    // Render again</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.count</span><span style="color:#FF79C6">++</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.odd </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.count </span><span style="color:#FF79C6">%</span><span style="color:#BD93F9"> 2</span><span style="color:#FF79C6"> ===</span><span style="color:#BD93F9"> 1</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // Do not trigger a new render</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.even </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.count </span><span style="color:#FF79C6">%</span><span style="color:#BD93F9"> 2</span><span style="color:#FF79C6"> ===</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Host</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span><span style="color:#FF79C6">{</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.odd</span><span style="color:#FF79C6">}</span><span style="color:#FF79C6"> {</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.count</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">Host</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    ;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>While in React functions you are going to use <code>hooks</code> to handle states and <code>interfaces</code> to declare your properties.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> React, {useEffect, useState} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">interface</span><span style="color:#8BE9FD;font-style:italic"> MyProps</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    count</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> MyComponent</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> React</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">FC</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">MyProps</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">props</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> MyProps</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> [odd, setOdd] </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useState</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">boolean</span><span style="color:#F8F8F2">>(</span><span style="color:#BD93F9">false</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> even </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    useEffect</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#6272A4">        // Render again</span></span>
<span class="line"><span style="color:#F8F8F2">        props.count</span><span style="color:#FF79C6">++</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#50FA7B">        setOdd</span><span style="color:#F8F8F2">(props.count </span><span style="color:#FF79C6">%</span><span style="color:#BD93F9"> 2</span><span style="color:#FF79C6"> ===</span><span style="color:#BD93F9"> 1</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">        // Do not trigger a new render</span></span>
<span class="line"><span style="color:#F8F8F2">        even </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> props.count </span><span style="color:#FF79C6">%</span><span style="color:#BD93F9"> 2</span><span style="color:#FF79C6"> ===</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }, [props.count]);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span><span style="color:#FF79C6">{</span><span style="color:#F8F8F2">odd</span><span style="color:#FF79C6">}</span><span style="color:#FF79C6"> {</span><span style="color:#F8F8F2">props.count</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/></span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> MyComponent;</span></span></code></pre><p>I now, I said I won’t cover hooks in this article, therefore let just summarize these as asynchronous functions, which observe or apply a change to a variable and in case of the hook dedicated to states, <code>useState</code> , trigger a new rendering if a change is applied to the observed variable.</p>
<hr />
<h3>Conditional Rendering</h3>
<p>Angular exposes is own tags which have to be use in the templates to perform any logical operations, notably <code>*ngIf</code> for conditional rendering.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">>Hello, World!&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> *ngIf</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">odd</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>{{count}}&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span></code></pre><p>One beauty of JSX is that you are not developing in a template, therefore you are using statements as you would do writing code.</p>
<p>In brief, a <code>if</code> is a <code>if</code> 😉.</p>
<p>Only important thing to remember about conditional rendering: always return something! That’s why, if you do not want to render anything, I suggest to return <code>undefined</code> which will have for effect to add nothing to the DOM.</p>
<p>With Stencil:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Host</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#FF79C6">    {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">      this</span><span style="color:#F8F8F2">.odd </span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span><span style="color:#FF79C6">{</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.odd</span><span style="color:#FF79C6">}</span><span style="color:#FF79C6"> {</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.count</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> undefined</span></span>
<span class="line"><span style="color:#FF79C6">    }</span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">Host</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Or with React:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;></span></span>
<span class="line"><span style="color:#FF79C6">        {</span><span style="color:#F8F8F2">odd </span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#FF79C6">                {</span><span style="color:#F8F8F2">odd</span><span style="color:#FF79C6">}</span><span style="color:#FF79C6"> {</span><span style="color:#F8F8F2">props.count</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        ) </span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> undefined</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/></span></span>
<span class="line"><span style="color:#F8F8F2">);</span></span></code></pre><p>Moreover, you can either inline your condition as above or use it wisely in split render methods.</p>
<p>As in this Stencil example:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Host</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#FF79C6">    {</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">renderLabel</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">Host</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">private </span><span style="color:#50FA7B">renderLabel</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.odd </span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span><span style="color:#FF79C6">{</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.odd</span><span style="color:#FF79C6">}</span><span style="color:#FF79C6"> {</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.count</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Or again in this React one:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">return</span><span style="color:#F8F8F2"> &#x3C;></span><span style="color:#FF79C6">{</span><span style="color:#50FA7B">renderLabel</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">&#x3C;/>;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">function</span><span style="color:#50FA7B"> renderLabel</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> odd </span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#FF79C6">            {</span><span style="color:#F8F8F2">odd</span><span style="color:#FF79C6">}</span><span style="color:#FF79C6"> {</span><span style="color:#F8F8F2">props.count</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    ) </span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>Summary</h3>
<p>There is so much left to say and to describe, but unfortunately I have to rush to make steps forwards in a useful, particularly in these special days, mobile application I am developing for a client.</p>
<p>If this appetizer made you eager to know more about JSX from an Angular point of view, let me know. I would be really happy to develop it further in several blog posts. And Like I said, I still got some more in order to accomplish my challenge 😃.</p>
<p>Stay home, stay safe!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Merge Two Objects And Array To Object In JavaScript]]></title>
          <description><![CDATA[How two merge two objects or transform an array into an object in JavaScript]]></description>
          <link>https://daviddalbusco.com/blog/merge-two-objects-and-array-to-object-in-javascript/</link>
          <pubDate>Thu, 02 Apr 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*RqxfxRKtnHkUdmbpCFhxSA.png" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@dargonesti?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Ludovic Migneault</a> on <a href="https://unsplash.com/s/photos/free?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I share <a href="https://daviddalbusco.com/blog/how-to-call-the-service-worker-from-the-web-app-context" rel="noopener noreferrer external">one trick a day</a> until the end of the COVID-19 quarantine in Switzerland, April 19th 2020. <strong>Seventeen</strong> days left until hopefully better days.</p>
<hr />
<p>To be really honest with you, I did not had that much idea for today’s blog post. For my defense, this is the eighteenth blog post I write every day in a row, it might explains my lack of today’s inspiration 😅.</p>
<p>That being said, I will share with you the following two tricks which I find useful.</p>
<hr />
<h3>Merge Two Objects</h3>
<p>Thanks to the introduction of <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax" rel="noopener noreferrer external">Spread Operator</a> in ES6, it is now more than ever before really easy to merge two objects. No more loops, comparison or rocket science, the merge operation can be written in a single line of code.</p>
<p>It is also worth to notice that in case both objects would contain the same key, the value of the last object, “the one on the right in the line of code”, is the winning value.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> bruno </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    sing</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    song</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Treasure</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> ratm </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    type</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">band</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    song</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Bombtrack</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> result </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> { </span><span style="color:#FF79C6">...</span><span style="color:#F8F8F2">bruno, </span><span style="color:#FF79C6">...</span><span style="color:#F8F8F2">ratm };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(result);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// -> {sing: true, song: "Bombtrack", type: "band"}</span></span></code></pre><hr />
<h3>Array To Object</h3>
<p>In order to transform an array to an object in JavaScript, I did not find so far the magic line of code which would do all the job in one single line of code. If you do know how to achieve this, ping me with your comments and best pieces of code, I would love to hear about it 😁.</p>
<p>Meanwhile, I use the <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce" rel="noopener noreferrer external">reduce</a> function. Fun fact, that’s probably one of the few times that I am using it. I use often <code>forEach</code> , <code>map</code> , <code>filter</code> and <code>find</code> but <code>reduce</code> , truly rarely.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">// {name: string, genre: string}[]</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> bands </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#F8F8F2">    {</span></span>
<span class="line"><span style="color:#F8F8F2">        name</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Ratm</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        genre</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">rock</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    },</span></span>
<span class="line"><span style="color:#F8F8F2">    {</span></span>
<span class="line"><span style="color:#F8F8F2">        name</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Bruno</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        genre</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Pop</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// {ratm: string, bruno: string}</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> artists </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> bands.</span><span style="color:#50FA7B">reduce</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">obj</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">item</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    obj[item.name] </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> item.genre;</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> obj;</span></span>
<span class="line"><span style="color:#F8F8F2">}, {});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(artists);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// {Ratm: "rock", Bruno: "Pop"}</span></span></code></pre><hr />
<h3>Summary</h3>
<p>It is interesting to notice that the more the language evolves, the more basic operations become easy to implement with few lines of code. Also interesting is the fact that sometimes it takes time to these new options to be rolled out to me. ES6 has been around for a while and I am still writing about it in 2020 😉.</p>
<p>Stay home, stay safe!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Test Angular Components and Services With HTTP Mocks]]></title>
          <description><![CDATA[How to test Angular Components and Services with the help of mocked HTTP requests]]></description>
          <link>https://daviddalbusco.com/blog/test-angular-components-and-services-with-http-requests-mocks/</link>
          <pubDate>Wed, 01 Apr 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*ARj7jYIrVb-eR7B0OOD2Zg.png" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@jramos10?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Josue Isai Ramos Figueroa</a> on <a href="https://unsplash.com/s/photos/free?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I share <a href="https://daviddalbusco.com/blog/how-to-call-the-service-worker-from-the-web-app-context" rel="noopener noreferrer external">one trick a day</a> until the end of the COVID-19 quarantine in Switzerland, April 19th 2020. <strong>Eighteen</strong> days left until hopefully better days.</p>
<hr />
<p>The other day I was writing some <a href="https://angular.io" rel="noopener noreferrer external">Angular</a> tests for a new project of one my client and I was about to mock my service function when suddenly the idea hit me: what if instead of mocking my service functions, I would mock the HTTP requests globally for all my tests with the goal to test also my services logic at the same time as I would test my components 🤔</p>
<p>I was able to achieve this goal and that’s why I’m sharing this learning in this new blog post.</p>
<hr />
<h3>Setup</h3>
<p>Let’s define a simple setup as example.</p>
<p>We have a <code>service</code> which exposes a single HTTP request. For the purpose of this tutorial, we can use the amazing free and open source API provided by the <a href="https://dog.ceo/dog-api/" rel="noopener noreferrer external">Dog API</a>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Injectable} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {HttpClient} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/common/http</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Observable} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">rxjs</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> interface</span><span style="color:#8BE9FD;font-style:italic"> Dog</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  message</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  status</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Injectable</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  providedIn</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">root</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> DogService</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  constructor</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">private</span><span style="color:#FFB86C;font-style:italic"> httpClient</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HttpClient</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  randomDog</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Observable</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Dog</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.httpClient</span></span>
<span class="line"><span style="color:#F8F8F2">               .</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Dog</span><span style="color:#F8F8F2">>(</span><span style="color:#F1FA8C">`https://dog.ceo/api/breeds/image/random`</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>And a component which displays the random doggo.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Component} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Observable} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">rxjs</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Dog, DogService} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">../dog.service</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  selector</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-dog</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  template</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> `&#x3C;img *ngIf="doggo$ | async as doggo"</span></span>
<span class="line"><span style="color:#F1FA8C">                  [src]="doggo.message">`</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> DogComponent</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  doggo$</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Observable</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Dog</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  constructor</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">private</span><span style="color:#FFB86C;font-style:italic"> dogService</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> DogService</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.doggo$ </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> dogService.</span><span style="color:#50FA7B">randomDog</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>If you test this component, rendered in your browser you should discover a good doggo like this sweet bulldog.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*nEw8NxG6y8xwPwOe9YXf4g.png" alt="" / loading="lazy"></p>
<hr />
<h3>Test Services With HTTP Requests</h3>
<p>As we are going to develop a mock for our HTTP requests, we can begin first by testing our service.</p>
<p>To test our service we are going to take advantages of the <a href="https://angular.io/api/common/http/testing/HttpClientTestingModule" rel="noopener noreferrer external">HttpClientTestingModule</a> provided by Angular as <a href="https://medium.com/@Jestfer?source=post_page-----3880ceac74cf----------------------" rel="noopener noreferrer external">Josué Estévez Fernández</a> described in his brillant article about <a href="https://medium.com/better-programming/testing-http-requests-in-angular-with-httpclienttestingmodule-3880ceac74cf" rel="noopener noreferrer external">Angular Testing</a>.</p>
<p>Basically, what we do is subscribing to our service exposed function <code>randomDog()</code> in order to except a result which should be our mocked data. To triggers the result we instruct the controller that we want to perform only one query using <code>exceptOne</code> and finally we <code>flush</code> the response with the mock data which will cause our observer to resolve.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { TestBed } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core/testing</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {HttpClientTestingModule, HttpTestingController}</span></span>
<span class="line"><span style="color:#FF79C6">       from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/common/http/testing</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Dog, DogService} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./dog.service</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> mockDog</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Dog</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    message</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#E9F284">    '</span><span style="color:#F1FA8C">https://images.dog.ceo/breeds/hound-basset/n02088238_9815.jpg</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    status</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">success</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">describe</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">DogService</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  let</span><span style="color:#F8F8F2"> httpTestingController</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HttpTestingController</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">  let</span><span style="color:#F8F8F2"> service</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> DogService</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  beforeEach</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    TestBed.</span><span style="color:#50FA7B">configureTestingModule</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">      providers</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [DogService],</span></span>
<span class="line"><span style="color:#F8F8F2">      imports</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [HttpClientTestingModule]</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    httpTestingController </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> TestBed.</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(HttpTestingController);</span></span>
<span class="line"><span style="color:#F8F8F2">    service </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> TestBed.</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(DogService);</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  afterEach</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    httpTestingController.</span><span style="color:#50FA7B">verify</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  it</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">should be created</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    expect</span><span style="color:#F8F8F2">(service).</span><span style="color:#50FA7B">toBeTruthy</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  it</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">random should should provide data</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    service.</span><span style="color:#50FA7B">randomDog</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">subscribe</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">dog</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Dog</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">      expect</span><span style="color:#F8F8F2">(dog).not.</span><span style="color:#50FA7B">toBe</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">null</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#50FA7B">      expect</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">stringify</span><span style="color:#F8F8F2">(dog)).</span><span style="color:#50FA7B">toEqual</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">stringify</span><span style="color:#F8F8F2">(mockDog));</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> req </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> httpTestingController</span></span>
<span class="line"><span style="color:#F8F8F2">              .</span><span style="color:#50FA7B">expectOne</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`https://dog.ceo/api/breeds/image/random`</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    req.</span><span style="color:#50FA7B">flush</span><span style="color:#F8F8F2">(mockDog);</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><p>If you run the tests (<code>npm run test</code> ) these should be successfull.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*Ko-H8QOYgTlx4xuB9hhhSQ.png" alt="" / loading="lazy"></p>
<hr />
<h3>Test Components With HTTP Requests Mock</h3>
<p>Now here comes the fun part 😉. Our goal is to test our component without “touching” the service but by mocking all HTTP requests used by these.</p>
<p>For such purpose we create a custom <code>HttpInterceptor</code> , as <a href="https://dev.to/sanidz" rel="noopener noreferrer external">sanidz</a> explained in his/her super article about <a href="https://dev.to/sanidz/angular-http-mock-interceptor-for-mocked-backend-1h5g" rel="noopener noreferrer external">Mocking Interceptor</a>, which should take care of, well, intercepting the requests and overriding our calls with our mock data when we have the need. In our example, if the DOG api is hit, we want to answer with the mock data we have defined earlier to test our service.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Injectable, Injector } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/common/http</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Observable, of } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">rxjs</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {mockDog} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./dog.service.spec</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Injectable</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> HttpRequestInterceptorMock</span><span style="color:#FF79C6"> implements</span><span style="color:#8BE9FD;font-style:italic"> HttpInterceptor</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    constructor</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">private</span><span style="color:#FFB86C;font-style:italic"> injector</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Injector</span><span style="color:#F8F8F2">) {}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    intercept</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">request</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HttpRequest</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">any</span><span style="color:#F8F8F2">>, </span><span style="color:#FFB86C;font-style:italic">next</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HttpHandler</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">              Observable</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">HttpEvent</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">any</span><span style="color:#F8F8F2">>> {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (request.url </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> request.url</span></span>
<span class="line"><span style="color:#F8F8F2">         .</span><span style="color:#50FA7B">indexOf</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`https://dog.ceo/api/breeds/image/random`</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">></span><span style="color:#FF79C6"> -</span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">            return</span></span>
<span class="line"><span style="color:#50FA7B">              of</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6;font-weight:bold">new</span><span style="color:#50FA7B"> HttpResponse</span><span style="color:#F8F8F2">({ status</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 200</span><span style="color:#F8F8F2">, body</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> mockDog }));</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> next.</span><span style="color:#50FA7B">handle</span><span style="color:#F8F8F2">(request);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>When creating the above interceptor you might face a typescript error regarding the decorator. If it is the case you can solve it by enabling <code>experimentalDecorators</code> in your <code>tsconfig.spec.json</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#8BE9FE">  "</span><span style="color:#8BE9FD">extends</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./tsconfig.json</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">  "</span><span style="color:#8BE9FD">compilerOptions</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">outDir</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./out-tsc/spec</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">experimentalDecorators</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">, </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">&#x3C;-</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">enable</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">experimental</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">decorator</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">types</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#E9F284">      "</span><span style="color:#F1FA8C">jasmine</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">      "</span><span style="color:#F1FA8C">node</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    ]</span></span>
<span class="line"><span style="color:#F8F8F2">  },</span></span>
<span class="line"><span style="color:#8BE9FE">  "</span><span style="color:#8BE9FD">files</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">src/test.ts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">src/polyfills.ts</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">  ],</span></span>
<span class="line"><span style="color:#8BE9FE">  "</span><span style="color:#8BE9FD">include</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">src/**/*.spec.ts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">src/**/*.d.ts</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">  ]</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Our interceptor being set, we can now test our component. Once again we are going to use the HttpClientTestingModule but moreover we are providing our HTTP interceptor for the configuration of the test. Doing so, on each request, our interceptor will be triggered and we are going to able to mock our data. We are also using these to ensure that our component’s image match the one we have defined as mock.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {async, ComponentFixture, TestBed}</span></span>
<span class="line"><span style="color:#FF79C6">       from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core/testing</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {HttpClientTestingModule}</span></span>
<span class="line"><span style="color:#FF79C6">       from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/common/http/testing</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {HTTP_INTERCEPTORS} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/common/http</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {HttpRequestInterceptorMock}</span></span>
<span class="line"><span style="color:#FF79C6">       from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">../http-request-interceptor.mock</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {mockDog} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">../dog.service.spec</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {DogComponent} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./dog.component</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">describe</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">DogComponent</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  let</span><span style="color:#F8F8F2"> component</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> DogComponent</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">  let</span><span style="color:#F8F8F2"> fixture</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> ComponentFixture</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">DogComponent</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  beforeEach</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">async</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    TestBed.</span><span style="color:#50FA7B">configureTestingModule</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">      declarations</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [DogComponent],</span></span>
<span class="line"><span style="color:#F8F8F2">      imports</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#F8F8F2">        HttpClientTestingModule</span></span>
<span class="line"><span style="color:#F8F8F2">      ],</span></span>
<span class="line"><span style="color:#F8F8F2">      providers</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#F8F8F2">        {</span></span>
<span class="line"><span style="color:#F8F8F2">          provide</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> HTTP_INTERCEPTORS,</span></span>
<span class="line"><span style="color:#F8F8F2">          useClass</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> HttpRequestInterceptorMock,</span></span>
<span class="line"><span style="color:#F8F8F2">          multi</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">      ]</span></span>
<span class="line"><span style="color:#F8F8F2">    }).</span><span style="color:#50FA7B">compileComponents</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  }));</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  beforeEach</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    fixture </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> TestBed.</span><span style="color:#50FA7B">createComponent</span><span style="color:#F8F8F2">(DogComponent);</span></span>
<span class="line"><span style="color:#F8F8F2">    component </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> fixture.componentInstance;</span></span>
<span class="line"><span style="color:#F8F8F2">    fixture.</span><span style="color:#50FA7B">detectChanges</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  it</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">should create</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    expect</span><span style="color:#F8F8F2">(component).</span><span style="color:#50FA7B">toBeTruthy</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  it</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">should render image</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, </span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> img</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLImageElement</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#F8F8F2">          fixture.debugElement.nativeElement.</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">img</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    expect</span><span style="color:#F8F8F2">(img).not.</span><span style="color:#50FA7B">toBe</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">null</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#50FA7B">    expect</span><span style="color:#F8F8F2">(mockDog.message </span><span style="color:#FF79C6">===</span><span style="color:#F8F8F2"> img.src).</span><span style="color:#50FA7B">toBe</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">true</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><p>That’s it, it is super, furthermore than being able to test our component we are also able to test our service at the same time 🥳.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*S9NnYvTRXnX0ztI0WZkkag.png" alt="" / loading="lazy"></p>
<hr />
<h3>Summary</h3>
<p>I’m really grateful to have find the useful tips from <a href="https://medium.com/@Jestfer?source=post_page-----3880ceac74cf----------------------" rel="noopener noreferrer external">Josué Estévez Fernández</a> and <a href="https://dev.to/sanidz" rel="noopener noreferrer external">sanidz</a>. The setup is now in place I can really progress in the development of the project while being able to add tests which made sense, at least to me 😉. I hope this approach will help you some day hopefully too.</p>
<p>Stay home, stay safe!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Add A Slider To You Angular App]]></title>
          <description><![CDATA[Use our core Web Component to add a slider to your application developed with Angular]]></description>
          <link>https://daviddalbusco.com/blog/add-a-slider-to-you-angular-app/</link>
          <pubDate>Tue, 31 Mar 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*fscs8H-c-4EwHK_3wDN1ow.png" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@persnicketyprints?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Persnickety Prints</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I share <a href="https://medium.com/@david.dalbusco/one-trick-a-day-d-34-469a0336a07e" rel="noopener noreferrer external">one trick a day</a> until the end of the COVID-19 quarantine in Switzerland, April 19th 2020. <strong>Nineteen</strong> days left until hopefully better days.</p>
<hr />
<p>The other day I was upgrading the dependencies of one of my client’s application developed with <a href="https://ionicframework.com" rel="noopener noreferrer external">Ionic</a>.</p>
<p>To my surprise, the “fade” transition of the <a href="https://ionicframework.com/docs/api/slides" rel="noopener noreferrer external">slider</a> wasn’t working anymore. After a bit of digging, I discovered, for performance reason, that the animations had been made optional and had to be solved by copying a piece of code in my application.</p>
<p>Moreover I also had to develop some design changes in its introduction process, which I did but actually not so straight forward as I assumed, as the slides are actually stacked when not displayed.</p>
<p>In short, it worked like a charm but that made me think, did you know that you can actually use the <strong>core</strong> Web Component of <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>, our open source editor for presentations, to add a slider to any web applications developed with or without any modern frontend frameworks?</p>
<p>Probably not 😉.</p>
<p>That’s why I’m going to display in this blog post, as for example, how you can do so in an application developed with <a href="https://angular.io" rel="noopener noreferrer external">Angular</a>.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*GywaedPi6bBVACpbGrZ5tA.gif" alt="" / loading="lazy"></p>
<p><em>Add a slider to your application</em></p>
<hr />
<h3>Installation</h3>
<p>For this realization, we are going to need our <a href="https://www.npmjs.com/package/@deckdeckgo/core" rel="noopener noreferrer external">core</a>. Moreover, because our solution is based on template, we are going to need at least one of them. As we are looking to implement a slider in which we are going to display information, the easiest template to use is our <a href="https://docs.deckdeckgo.com/slides/title" rel="noopener noreferrer external">title</a> template which centers the content.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> i</span><span style="color:#F1FA8C"> @deckdeckgo/core</span><span style="color:#F1FA8C"> @deckdeckgo/slide-title</span><span style="color:#BD93F9"> --save</span></span></code></pre><p>Furthermore, it may complains at build time that some type definitions are missing no worries, just add these to your development dependencies.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> i</span><span style="color:#F1FA8C"> @deckdeckgo/types</span><span style="color:#BD93F9"> --save-dev</span></span></code></pre><hr />
<h3>Configuration</h3>
<p>Once installed we need to import the components. These are developed with <a href="https://stenciljs.com" rel="noopener noreferrer external">StencilJS</a> which provides a handy <a href="https://stenciljs.com/docs/overview" rel="noopener noreferrer external">documentation</a> on the subject.</p>
<p>That being said, Angular won’t resolve the libraries if we only import these as the following.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@deckdeckgo/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@deckdeckgo/slide-title</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span></code></pre><p>That’s why we are using the provided loaders option.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { </span><span style="color:#FFB86C;font-style:italic">defineCustomElements</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> deckGoCore } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@deckdeckgo/core/dist/loader</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { </span><span style="color:#FFB86C;font-style:italic">defineCustomElements</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> deckGoSlide } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@deckdeckgo/slide-title/dist/loader</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">deckGoCore</span><span style="color:#F8F8F2">(window);</span></span>
<span class="line"><span style="color:#50FA7B">deckGoSlide</span><span style="color:#F8F8F2">(window);</span></span></code></pre><p>Furthermore, because Angular is not going to recognize our templates, we have to instruct it to support custom schemas. This can be set in any modules or globally in <code>app.module.ts</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { BrowserModule } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@angular/platform-browser</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { CUSTOM_ELEMENTS_SCHEMA, NgModule } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { AppRoutingModule } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./app-routing.module</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { AppComponent } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./app.component</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">NgModule</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    declarations</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#50FA7B;font-style:italic">AppComponent</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">    imports</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#50FA7B;font-style:italic">BrowserModule</span><span style="color:#F8F8F2">, </span><span style="color:#50FA7B;font-style:italic">AppRoutingModule</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">    providers</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [],</span></span>
<span class="line"><span style="color:#F8F8F2">    bootstrap</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#50FA7B;font-style:italic">AppComponent</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">    schemas</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [CUSTOM_ELEMENTS_SCHEMA]</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> AppModule</span><span style="color:#F8F8F2"> {}</span></span></code></pre><hr />
<h3>Usage</h3>
<p>Our components are installed and configured, we can add the slider which is nothing else that a container <code>&lt;deckgo-deck/&gt;</code> , and its children, the slides, <code>&lt;deckgo-slide-title&gt;</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">deckgo-deck</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">deckgo-slide-title</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">h1</span><span style="color:#50FA7B;font-style:italic"> slot</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">title</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Add&#x3C;/</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">deckgo-slide-title</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">deckgo-slide-title</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">h1</span><span style="color:#50FA7B;font-style:italic"> slot</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">title</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>a slider&#x3C;/</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">deckgo-slide-title</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">deckgo-slide-title</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">h1</span><span style="color:#50FA7B;font-style:italic"> slot</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">title</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>to your app&#x3C;/</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">deckgo-slide-title</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">deckgo-deck</span><span style="color:#F8F8F2">></span></span></code></pre><p>And that’s it, the slider is in place 🎉.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*CpmsgBb-IK33HOxkJA-eNw.gif" alt="" / loading="lazy"></p>
<p><em>Our slider is ready</em></p>
<hr />
<h3>Customization</h3>
<p>You might want to apply some styling to your slide, here are some options.</p>
<hr />
<h4>Hide Pager</h4>
<p>A pager is useful for a presentation but most probably not for a slider in an application. That’s why you might want to hide it using the CSS4 variables <code>--pager-display</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">deckgo-deck</span><span style="color:#50FA7B;font-style:italic"> style</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">--pager-display: none;</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">> &#x3C;/</span><span style="color:#FF79C6">deckgo-deck</span><span style="color:#F8F8F2">></span></span></code></pre><p><img src="https://daviddalbusco.com/assets/images/1*GywaedPi6bBVACpbGrZ5tA.gif" alt="" / loading="lazy"></p>
<p><em>Hidden pager</em></p>
<hr />
<h4>Transition</h4>
<p>You might want to use another transition effect. Our core doesn’t handle yet a lot of different animation, <a href="https://github.com/deckgo/deckdeckgo" rel="noopener noreferrer external">Pull Requests</a> are welcomed, but it does already offers a <code>fade</code> effect or <code>none</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">deckgo-deck</span><span style="color:#50FA7B;font-style:italic"> style</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">--pager-display: none;</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> transition</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">fade</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">> &#x3C;/</span><span style="color:#FF79C6">deckgo-deck</span><span style="color:#F8F8F2">></span></span></code></pre><p><img src="https://daviddalbusco.com/assets/images/1*ZHlq-pmWawTDVYDKUxG8og.gif" alt="" / loading="lazy"></p>
<p><em>Fade transition</em></p>
<hr />
<h3>Navigation</h3>
<p>If we would implement the slide as an introduction to your application, there is a good chance that we would be interested to add a <code>next</code> and a <code>skip</code> buttons. Our core also emits multiple <a href="https://docs.deckdeckgo.com/deck/events" rel="noopener noreferrer external">events</a> and exposes <a href="https://docs.deckdeckgo.com/deck/navigation" rel="noopener noreferrer external">navigation</a> methods, let’s try to use these.</p>
<p>For such purpose, we add these two buttons, we are identifying our deck with <code>#deck</code> and are listening to two deck transitions events respectively <code>slideNextDidChange</code> and <code>slidePrevDidChange</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">deckgo-deck</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    style</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">--pager-display: none;</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    transition</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">fade</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    #deck</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    (slideNextDidChange)</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">updateLastSlide()</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    (slidePrevDidChange)</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">updateLastSlide()</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">deckgo-slide-title</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> slot</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">title</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">>Add&#x3C;/</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">deckgo-slide-title</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">deckgo-slide-title</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> slot</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">title</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">>a slider&#x3C;/</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">deckgo-slide-title</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">deckgo-slide-title</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> slot</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">title</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">>to your app&#x3C;/</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">deckgo-slide-title</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">deckgo-deck</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> style</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">position: absolute; bottom: 0; right: 0;</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">button</span><span style="color:#50FA7B;font-style:italic"> (click)</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">skip()</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> *ngIf</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">!lastSlide</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Skip&#x3C;/</span><span style="color:#FF79C6">button</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">button</span><span style="color:#50FA7B;font-style:italic"> (click)</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">next()</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Next&#x3C;/</span><span style="color:#FF79C6">button</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span></code></pre><p>Finally we implement the related methods while using a <code>ViewChild</code> reference on the deck which exposes the methods we need.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Component, ElementRef, ViewChild} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {</span><span style="color:#FFB86C;font-style:italic">defineCustomElements</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> deckGoCore} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@deckdeckgo/core/dist/loader</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {</span><span style="color:#FFB86C;font-style:italic">defineCustomElements</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> deckGoSlide} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@deckdeckgo/slide-title/dist/loader</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">deckGoCore</span><span style="color:#F8F8F2">(window);</span></span>
<span class="line"><span style="color:#50FA7B">deckGoSlide</span><span style="color:#F8F8F2">(window);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  selector</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-root</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  templateUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./app.component.html</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  styleUrls</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">./app.component.scss</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">]</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> AppComponent</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  @</span><span style="color:#50FA7B">ViewChild</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">deck</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">) deck</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> ElementRef</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  lastSlide </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  async</span><span style="color:#50FA7B"> skip</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Go to next page</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  async</span><span style="color:#50FA7B"> next</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> end </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.deck.nativeElement.</span><span style="color:#50FA7B">isEnd</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (end) {</span></span>
<span class="line"><span style="color:#FF79C6">      await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">skip</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">      return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.deck.nativeElement.</span><span style="color:#50FA7B">slideNext</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  async</span><span style="color:#50FA7B"> updateLastSlide</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> index </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.deck.nativeElement.</span><span style="color:#50FA7B">getActiveIndex</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> length </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.deck.nativeElement.</span><span style="color:#50FA7B">getLength</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.lastSlide </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> index </span><span style="color:#FF79C6">===</span><span style="color:#F8F8F2"> length </span><span style="color:#FF79C6">-</span><span style="color:#BD93F9"> 1</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>And that’s it, it works out 😁.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*r1I_EPMYsMgQxoGMA4Uk8Q.gif" alt="" / loading="lazy"></p>
<p><em>Navigation with our core</em></p>
<hr />
<h3>Summary</h3>
<p>It isn’t our goal to provide the most complete core slider with a zillion options as it would not serve our main focus but, that being said, as you may have noticed, I think it offers a valid alternative to implement quickly a slider in any modern web applications.</p>
<p>If you want to know more about it, check out our <a href="https://docs.deckdeckgo.com" rel="noopener noreferrer external">documentation</a> or give a try to our <a href="https://deckdeckgo.com" rel="noopener noreferrer external">editor</a> for your next presentations!</p>
<p>Stay home, stay safe!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Create A Modal For Your Angular App Without Libs]]></title>
          <description><![CDATA[How to create a modal for your application developed with Angular without the use of any libraries or dependencies]]></description>
          <link>https://daviddalbusco.com/blog/create-a-modal-for-your-angular-app-without-libs/</link>
          <pubDate>Mon, 30 Mar 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*xNE1YF5sWgrqysu1GqwUNA.png" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@roger_burkhard?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Roger Burkhard</a> on <a href="https://unsplash.com/s/photos/free?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I share <a href="https://daviddalbusco.com/blog/how-to-call-the-service-worker-from-the-web-app-context" rel="noopener noreferrer external">one trick a day</a> until the end of the COVID-19 quarantine in Switzerland, April 19th 2020. <strong>Twenty</strong> days left until hopefully better days.</p>
<hr />
<p>The other day we were building a quick proof of <a href="https://angular.io" rel="noopener noreferrer external">Angular</a> concept with one of my client’s teammate for which we had to display something in a modal. Instead of installing and using some design libraries to solve this requirement, I estimated that it would need almost the same effort to create quickly one (I was not wrong on that one).</p>
<p>Yesterday I <a href="https://daviddalbusco.com/blog/create-a-menu-for-your-gatsby-website-without-libs" rel="noopener noreferrer external">shared another solution</a> to create a custom menu for a <a href="https://www.gatsbyjs.org" rel="noopener noreferrer external">Gatsby</a> website without any dependencies and that’s why I had the idea today to share the following tutorial.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*s0eN_t22IJLbosGLHblfiw.gif" alt="" / loading="lazy"></p>
<hr />
<h3>Service</h3>
<p>The modal has two states: <code>open</code> and <code>close</code> . That’s why, before anything else, we create a <code>service</code> using the Angular CLI (command: <code>ng g service modal)</code> which we are going to use across our application to trigger either its opening or closing.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Injectable} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {BehaviorSubject, Observable} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">rxjs</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Injectable</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  providedIn</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">root</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> ModalService</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#F8F8F2"> display</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> BehaviorSubject</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">open</span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> |</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">close</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#FF79C6;font-weight:bold">                   new</span><span style="color:#50FA7B"> BehaviorSubject</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">close</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  watch</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Observable</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">open</span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> |</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">close</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.display.</span><span style="color:#50FA7B">asObservable</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  open</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.display.</span><span style="color:#50FA7B">next</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">open</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  close</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.display.</span><span style="color:#50FA7B">next</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">close</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Note that at the end of the day, you can use a <code>boolean</code> or an <code>enum</code> if you rather like, or a <code>Subject</code> instead of <code>BehaviorSubject</code> . What does matter is to be able to maintain the two states of the modal.</p>
<hr />
<h3>Modal</h3>
<p>We create a new component for our modal using the Angular CLI (<code>ng c component modal</code> ).</p>
<hr />
<h4>Code</h4>
<p>The component code contains a variable, an Observable which we instantiate to watch out the state of the modal, and exposes a function which we can use to close the modal.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Component, OnInit} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Observable} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">rxjs</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {ModalService} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">../modal.service</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  selector</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-modal</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  templateUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./modal.component.html</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  styleUrls</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">./modal.component.scss</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> ModalComponent</span><span style="color:#FF79C6"> implements</span><span style="color:#8BE9FD;font-style:italic"> OnInit</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  display$</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Observable</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">open</span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> |</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">close</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  constructor</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#FF79C6">      private</span><span style="color:#FFB86C;font-style:italic"> modalService</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> ModalService</span></span>
<span class="line"><span style="color:#F8F8F2">  ) {}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  ngOnInit</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.display$ </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.modalService.</span><span style="color:#50FA7B">watch</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  close</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.modalService.</span><span style="color:#50FA7B">close</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h4>Template</h4>
<p>In the container we define a <code>section</code> to cover the all screen when the modal is opened and we define a child, a <code>div</code> , to constrain our modal to a certain size.</p>
<p>Note that I stop the event propagation on the container just in case you would like to add some actions inside the modal, for example a form with a submit button.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">ng-container</span><span style="color:#50FA7B;font-style:italic"> *ngIf</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">display$ | async as display</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">section</span><span style="color:#50FA7B;font-style:italic"> [class.open]</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">display === 'open'</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> (click)</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">close()</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> (click)</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">$event.stopPropagation()</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">button</span><span style="color:#50FA7B;font-style:italic"> class</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">close</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> type</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">button</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> (click)</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">close()</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>X&#x3C;/</span><span style="color:#FF79C6">button</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">>Hello World&#x3C;/</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">section</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">ng-container</span><span style="color:#F8F8F2">></span></span></code></pre><hr />
<h4>Style</h4>
<p>To make the modal appears smoothly we use a brief <code>transition</code> on the property <code>opacity</code> . We also define some <code>box-shadow</code> and and <code>background</code> for the backdrop to make thing just a bit styled.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">section</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">{</span></span>
<span class="line"><span style="color:#8BE9FD">  visibility</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">hidden</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">;</span></span>
<span class="line"><span style="color:#8BE9FD">  opacity</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">0</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  &#x26;</span><span style="color:#50FA7B;font-style:italic">.open</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">{</span></span>
<span class="line"><span style="color:#8BE9FD">    visibility</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">inherit</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">;</span></span>
<span class="line"><span style="color:#8BE9FD">    opacity</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">1</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">;</span></span>
<span class="line"><span>  </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">  display</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">block</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">  position</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">absolute</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">;</span></span>
<span class="line"><span style="color:#8BE9FD">  top</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">0</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">;</span></span>
<span class="line"><span style="color:#8BE9FD">  left</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">0</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">;</span></span>
<span class="line"><span style="color:#8BE9FD">  right</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">0</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">;</span></span>
<span class="line"><span style="color:#8BE9FD">  bottom</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">0</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">  background</span><span style="color:#F8F8F2">: </span><span style="color:#50FA7B">rgba(</span><span style="color:#BD93F9">0</span><span style="color:#6272A4">,</span><span style="color:#BD93F9"> 0</span><span style="color:#6272A4">,</span><span style="color:#BD93F9"> 0</span><span style="color:#6272A4">,</span><span style="color:#BD93F9"> 0.2</span><span style="color:#50FA7B">)</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">;</span></span>
<span class="line"><span style="color:#8BE9FD">  transition</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">opacity</span><span style="color:#BD93F9"> 250</span><span style="color:#FF79C6">ms</span><span style="color:#BD93F9"> ease-in</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  ></span><span style="color:#FF79C6"> div</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">{</span></span>
<span class="line"><span style="color:#8BE9FD">    position</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">absolute</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">;</span></span>
<span class="line"><span style="color:#8BE9FD">    top</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">50</span><span style="color:#FF79C6">%</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">;</span></span>
<span class="line"><span style="color:#8BE9FD">    left</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">50</span><span style="color:#FF79C6">%</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">;</span></span>
<span class="line"><span style="color:#8BE9FD">    transform</span><span style="color:#F8F8F2">: </span><span style="color:#50FA7B">translate(</span><span style="color:#BD93F9">-50</span><span style="color:#FF79C6">%</span><span style="color:#6272A4">,</span><span style="color:#BD93F9"> -50</span><span style="color:#FF79C6">%</span><span style="color:#50FA7B">)</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">    height</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">200</span><span style="color:#FF79C6">px</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">;</span></span>
<span class="line"><span style="color:#8BE9FD">    width</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">300</span><span style="color:#FF79C6">px</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">    background</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">white</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">;</span></span>
<span class="line"><span style="color:#8BE9FD">    padding</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">64</span><span style="color:#FF79C6">px</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">    border-radius</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">8</span><span style="color:#FF79C6">px</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">    box-shadow</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">0</span><span style="color:#BD93F9"> 0</span><span style="color:#BD93F9"> 8</span><span style="color:#FF79C6">px</span><span style="color:#BD93F9"> 4</span><span style="color:#FF79C6">px</span><span style="color:#50FA7B"> rgba(</span><span style="color:#BD93F9">0</span><span style="color:#6272A4">,</span><span style="color:#BD93F9"> 0</span><span style="color:#6272A4">,</span><span style="color:#BD93F9"> 0</span><span style="color:#6272A4">,</span><span style="color:#BD93F9"> 0.1</span><span style="color:#50FA7B">)</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">;</span></span>
<span class="line"><span>  </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  button</span><span style="color:#50FA7B;font-style:italic">.close</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">{</span></span>
<span class="line"><span style="color:#8BE9FD">    background</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">none</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">;</span></span>
<span class="line"><span style="color:#8BE9FD">    color</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">inherit</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">;</span></span>
<span class="line"><span style="color:#8BE9FD">    border</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">none</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">;</span></span>
<span class="line"><span style="color:#8BE9FD">    font</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">inherit</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">;</span></span>
<span class="line"><span style="color:#8BE9FD">    cursor</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">pointer</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">;</span></span>
<span class="line"><span style="color:#8BE9FD">    outline</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">none</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">    position</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">absolute</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">;</span></span>
<span class="line"><span style="color:#8BE9FD">    top</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">0</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">;</span></span>
<span class="line"><span style="color:#8BE9FD">    left</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">0</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">    padding</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">16</span><span style="color:#FF79C6">px</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">;</span></span>
<span class="line"><span>  </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">}</span></span>
<span class="line"><span style="color:#FF5555;font-style:italic;text-decoration:underline">}</span></span></code></pre><hr />
<h4>Declaration</h4>
<p>For simplicity reason I didn’t created a module to load the modal but if you would follow this tutorial for a real life application, I would advise you to do so in order to lazy load it. Meanwhile, in this blog post, we add our component to the <code>declarations</code> of our main module <code>app.module.ts.</code></p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">NgModule</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  declarations</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#50FA7B;font-style:italic">AppComponent</span><span style="color:#F8F8F2">, </span><span style="color:#50FA7B;font-style:italic">ModalComponent</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#FF79C6">  ...</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> AppModule</span><span style="color:#F8F8F2"> {}</span></span></code></pre><p>Additionally, we also use our component in our template only once for our all application, as we only manage a single instance and state, for example in <code>app.component.html</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">router-outlet</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">router-outlet</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">app-modal</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">app-modal</span><span style="color:#F8F8F2">></span></span></code></pre><h3>Usage</h3>
<p>We are set, everything is developed, we just need now to effectively test it. That’s why we add a <code>button</code> to our app which triggers the modal opening.</p>
<p>For example, we can declare a new function <code>open()</code> in one of our component in which we are looking to trigger the modal opening.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Component} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {ModalService} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">../modal.service</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  selector</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-home</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  templateUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">home.page.html</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  styleUrls</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">home.page.scss</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> HomePage</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  constructor</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#FF79C6">      private</span><span style="color:#FFB86C;font-style:italic"> modalService</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> ModalService</span></span>
<span class="line"><span style="color:#F8F8F2">  ) {}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  open</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.modalService.</span><span style="color:#50FA7B">open</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>And link the method in the related template.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">button</span><span style="color:#50FA7B;font-style:italic"> (click)</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">open()</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">h2</span><span style="color:#F8F8F2">>Open&#x3C;/</span><span style="color:#FF79C6">h2</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">button</span><span style="color:#F8F8F2">></span></span></code></pre><p>That’s it, nothing more, nothing less, we have developed a custom modal for our application without any JavaScript dependencies 😁.</p>
<hr />
<h3>Summary</h3>
<p>Of course out of the box, a quickly custom made modal isn’t the most beautiful one you ever used or saw, but to me, what’s important to communicate is probably the fact that we don’t have always to rely on dependencies. The path is probably Peter Quill’s one, a bit of both 😉.</p>
<p>Stay home, stay safe!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Create A Menu For Your Gatsby Website Without Libs]]></title>
          <description><![CDATA[How to create a menu for your Gatsby website without the use of any libraries or dependencies]]></description>
          <link>https://daviddalbusco.com/blog/create-a-menu-for-your-gatsby-website-without-libs/</link>
          <pubDate>Sun, 29 Mar 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*yi2pw2XA-49C9nGuXudI1A.png" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@catherineheath?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Catherine Heath</a> on <a href="https://unsplash.com/s/photos/menu?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I share <a href="https://daviddalbusco.com/blog/how-to-call-the-service-worker-from-the-web-app-context" rel="noopener noreferrer external">one trick a day</a> until the end of the COVID-19 quarantine in Switzerland, April 19th 2020. <strong>Twenty-one</strong> days left until hopefully better days.</p>
<hr />
<p>I have developed my <a href="https://daviddalbusco.com" rel="noopener noreferrer external">personal</a> website with <a href="https://www.gatsbyjs.org" rel="noopener noreferrer external">Gatsby</a> but without using any templates or any design libraries. No particular reason, I just like in my personal projects to restrict the use of dependencies when I can. Thereby, I had to build my own simple menu for the navigation which I’m sharing with you today.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*dhsTKRMX2hNVqfmaDGwaOg.gif" alt="" / loading="lazy"></p>
<p><em><a href="https://daviddalbusco.com" rel="noopener noreferrer external">daviddalbusco.com</a></em></p>
<hr />
<h3>Menu Component</h3>
<p>The menu is nothing else than a component which contains a <code>state</code> to reflect its status, being open or close. Its only particularity is the fact that we have to expose a function <code>open()</code> in order to be able to trigger its opening from the navigation, in my case, from the hamburger menu in the navigation bar. For such purpose we use Hooks API <a href="https://reactjs.org/docs/hooks-reference.html#useimperativehandle" rel="noopener noreferrer external">useImperativeHandle</a> to customize our instance.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> React, { useImperativeHandle, useRef } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Link } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">gatsby</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> styles </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./menu.module.scss</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">class</span><span style="color:#8BE9FD"> Menu</span><span style="color:#FF79C6"> extends</span><span style="color:#8BE9FD;font-style:italic"> React</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">Component</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    constructor</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">props</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        super</span><span style="color:#F8F8F2">(props);</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        this</span><span style="color:#F8F8F2">.state </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">            open</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> false</span></span>
<span class="line"><span style="color:#F8F8F2">        };</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">div</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                role</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">button</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                tabIndex</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">0</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                className</span><span style="color:#FF79C6">={</span><span style="color:#F1FA8C">`</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">styles.menu</span><span style="color:#FF79C6">}</span><span style="color:#FF79C6"> ${</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.state.open</span><span style="color:#FF79C6"> ?</span><span style="color:#F1FA8C"> `</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">styles.open</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#FF79C6"> :</span><span style="color:#E9F284"> ""</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                onClick</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">close</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                onKeyDown</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">close</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#F8F8F2">            ></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Link</span><span style="color:#50FA7B;font-style:italic"> to</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">>Home&#x3C;/</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">Link</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Link</span><span style="color:#50FA7B;font-style:italic"> to</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/#portfolio</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">>Portfolio&#x3C;/</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">Link</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Link</span><span style="color:#50FA7B;font-style:italic"> to</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/#about</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">>About&#x3C;/</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">Link</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Link</span><span style="color:#50FA7B;font-style:italic"> to</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/blog</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">>Blog&#x3C;/</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">Link</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Link</span><span style="color:#50FA7B;font-style:italic"> to</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/#contact</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">>Contact&#x3C;/</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">Link</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        );</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    close</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">setState</span><span style="color:#F8F8F2">({ open</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2"> });</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    open</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">setState</span><span style="color:#F8F8F2">({ open</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2"> });</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> React.</span><span style="color:#50FA7B">forwardRef</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">props</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">ref</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> menuRef </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useRef</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    useImperativeHandle</span><span style="color:#F8F8F2">(ref, () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> ({</span></span>
<span class="line"><span style="color:#50FA7B">        open</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#F8F8F2">            menuRef.current.</span><span style="color:#50FA7B">open</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    }));</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Menu</span><span style="color:#50FA7B;font-style:italic"> ref</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">menuRef</span><span style="color:#FF79C6">}</span><span style="color:#FF79C6"> {...</span><span style="color:#F8F8F2">props</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2"> />;</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><p>For the styling, I use CSS modules in the above example. The menu is as a fixed <code>div</code> which covers the all screen and is per default not visible. With the help of the state, I apply a style to modify its opacity with a small transition to make the menu appears a bit smoothly.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B;font-style:italic">.menu</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD">    position</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> fixed</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">    top</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">    left</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">    height</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 100</span><span style="color:#FF79C6">%</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">    width</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 100</span><span style="color:#FF79C6">%</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">    background</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> #ffffff</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">    z-index</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 1031</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">    display</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> flex</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">    flex-direction</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> column</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">    justify-content</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> center</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">    align-items</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> center</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">    visibility</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> hidden</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">    opacity</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">    transition</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> all</span><span style="color:#BD93F9"> 0.35</span><span style="color:#FF79C6">s</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">    cursor</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> pointer</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    &#x26;.open {</span></span>
<span class="line"><span style="color:#8BE9FD">        visibility</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> visible</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">        opacity</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 1</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    a</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD">        pointer-events</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> all</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>Integrating The Menu</h3>
<p>To use the menu in the navigation, I have integrated in its related component. The only particularity I had to develop is assigning a reference in order to be able call the method <code>open()</code> from an <code>onClick</code> function.</p>
<p>Note that as the menu inherits a <code>fixed</code> style, it can be integrated at any level of the DOM.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> React </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> Menu </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./menu</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">class</span><span style="color:#8BE9FD"> Navigation</span><span style="color:#FF79C6"> extends</span><span style="color:#8BE9FD;font-style:italic"> React</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">Component</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF79C6">button</span><span style="color:#50FA7B;font-style:italic"> onClick</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">toggleMenu</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">>Open Menu&#x3C;/</span><span style="color:#FF79C6">button</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Menu</span><span style="color:#50FA7B;font-style:italic"> ref</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">el</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> (</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.childMenu </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> el)</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/></span></span>
<span class="line"><span style="color:#F8F8F2">        );</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    toggleMenu</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        this</span><span style="color:#F8F8F2">.childMenu.</span><span style="color:#50FA7B">open</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> Navigation;</span></span></code></pre><p>That’s it, nothing more, nothing less, we have developed a custom menu for our website without any JavaScript dependencies 😁.</p>
<hr />
<h3>Summary</h3>
<p>I like to use Gatsby for website. It has many advantages and I think it is also cool for me that it uses <a href="https://reactjs.org" rel="noopener noreferrer external">React</a> as I like on a weekly basis to not stick to the same technologies. A bit of <a href="https://angular.io" rel="noopener noreferrer external">Angular</a>, a bit of React, a bit of Web Components with <a href="https://stenciljs.com" rel="noopener noreferrer external">StencilJS</a>, a bit of vanilla JavaScript, everything is only fun 😉.</p>
<p>Stay home, stay safe!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Protect Your HTTP Firebase Cloud Functions]]></title>
          <description><![CDATA[How to protect your HTTP Firebase Cloud Functions with an authorization bearer]]></description>
          <link>https://daviddalbusco.com/blog/protect-your-http-firebase-cloud-functions/</link>
          <pubDate>Sat, 28 Mar 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*ScsCTy63_eHnGH57WbJcLQ.png" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@dre0316?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Andre Hunter</a> on <a href="https://unsplash.com/s/photos/free?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I share <a href="https://daviddalbusco.com/blog/how-to-call-the-service-worker-from-the-web-app-context" rel="noopener noreferrer external">one trick a day</a> until the end of the COVID-19 quarantine in Switzerland, April 19th 2020. <strong>Twenty-two</strong> days left until hopefully better days.</p>
<hr />
<p>Last year I developed an application for a foundation which has for goal to help people from a certain age. Mostly for administrative reason, the project was not yet released publicly.</p>
<p>Recently we noticed some similarities in its goal and the current lockdown situation. That’s why I was asked to create a spin-off, containing the useful features, which can be unleashed quickly because it would be useful!</p>
<p>I spent my Saturday morning “cloning” our <a href="https://firebase.google.com" rel="noopener noreferrer external">Firebase Cloud</a> infrastructure and had to protect the new <a href="https://cloud.google.com/functions/docs/writing/http" rel="noopener noreferrer external">HTTP Functions</a> I just deployed online. That’s why I had the idea to write this brief article about the subject.</p>
<hr />
<h3>Authorization Bearer</h3>
<p>When you create Firebase Cloud function which can be triggered or called through an HTTP request, the function is public and available on the internet. As far as I know, its generated URL does not contains any random id or other hash which would make the route not predictable, therefore you have to find a way to protect it.</p>
<p>That’s why the solution, in my point of view, is to authenticate each requests with an authorization token.</p>
<p>One way of solving this is adding a constant key in your code. For example, if we have deployed the following function.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#BD93F9"> *</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> functions </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">firebase-functions</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> helloWorld </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> functions.https.</span><span style="color:#50FA7B">onRequest</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">request</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">response</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    response.</span><span style="color:#50FA7B">json</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        result</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> `Hello World.`</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><p>We can create a function to validate a <code>bearer</code> which would have to be provided for each requests.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Request} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">firebase-functions/lib/providers/https</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> validBearer</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">request</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Request</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">boolean</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> key</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">our-key-value</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> authorization </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> request.</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Authorization</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> split </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#F8F8F2">          authorization </span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2"> authorization.</span><span style="color:#50FA7B">split</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Bearer </span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [];</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> bearerKey </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#F8F8F2">          split </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> split.length </span><span style="color:#FF79C6">>=</span><span style="color:#BD93F9"> 2</span><span style="color:#FF79C6"> ?</span><span style="color:#F8F8F2"> split[</span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">] </span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> key </span><span style="color:#FF79C6">===</span><span style="color:#F8F8F2"> bearerKey;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>And use it to extend our HTTP function with a test.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> helloWorld </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#F8F8F2">    functions.https.</span><span style="color:#50FA7B">onRequest</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">request</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">response</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">      const</span><span style="color:#F8F8F2"> isValidBearer</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> validBearer</span><span style="color:#F8F8F2">(request);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">      if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">isValidBearer) {</span></span>
<span class="line"><span style="color:#F8F8F2">        response.</span><span style="color:#50FA7B">status</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">400</span><span style="color:#F8F8F2">).</span><span style="color:#50FA7B">json</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">            error</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">Not Authorized</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">      }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">      response.</span><span style="color:#50FA7B">json</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        result</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> `Hello World.`</span></span>
<span class="line"><span style="color:#F8F8F2">      });</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><p>For the <code>key</code> we can of course use something like a password or dumb keyword as I used above but it would be more secure to use for example a Version 4 UUID. There are many tools to generate such but I used today <a href="https://www.uuidgenerator.net" rel="noopener noreferrer external">https://www.uuidgenerator.net</a> which perfectly did the job.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> key </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">975dd9f6-4a89-4825-9a6d-deae71304a29</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span></code></pre><p>As a result, our HTTP route is now protected and only accessible if an authorization is provided.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">#!/bin/sh</span></span>
<span class="line"><span style="color:#50FA7B">curl</span><span style="color:#BD93F9"> -i</span></span>
<span class="line"><span style="color:#50FA7B">     -H</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Accept: application/json</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B">     -H</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Authorization: Bearer 975dd9f6-4a89-4825-9a6d-deae71304a29</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B">     -X</span><span style="color:#F1FA8C"> GET</span><span style="color:#F1FA8C">  https://us-central1-yolo.cloudfunctions.net/helloWorld</span></span></code></pre><hr />
<h3>Firebase Environment Variables</h3>
<p>Protection is cool, but is our protection protected 🧐? If our code is published as an open source software, our key is going to be exposed. Moreover, it ain’t really cute to handle a key in middle of the code. But there is a solution.</p>
<p>Firebase provides the ability to define not publicly exposed environment variables which can be read from Cloud functions.</p>
<p>To define our above key as such we can run the following command with the help of the Firebase CLI.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">#!/bin/sh</span></span>
<span class="line"><span style="color:#50FA7B">firebase</span><span style="color:#F1FA8C"> functions:config:set</span><span style="color:#F1FA8C"> hello.world.key=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">975dd9f6-4a89-4825-9a6d-deae71304a29</span><span style="color:#E9F284">"</span></span></code></pre><p>Last remaining things to do is replacing our constant with the new variable.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> key </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> functions.</span><span style="color:#50FA7B">config</span><span style="color:#F8F8F2">().hello.world.key;</span></span></code></pre><p>And that’s it, our HTTP Firebase Cloud function is protected 🎉.</p>
<hr />
<h3>Altogether</h3>
<p>Just in case you would need the above code in one block, here is it altogether:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#BD93F9"> *</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> functions </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">firebase-functions</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Request} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">firebase-functions/lib/providers/https</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> validBearer</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">request</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Request</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">boolean</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> key </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> functions.</span><span style="color:#50FA7B">config</span><span style="color:#F8F8F2">().hello.world.key;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> authorization </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> request.</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Authorization</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> split </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#F8F8F2">          authorization </span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2"> authorization.</span><span style="color:#50FA7B">split</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Bearer </span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [];</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> bearerKey </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#F8F8F2">          split </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> split.length </span><span style="color:#FF79C6">>=</span><span style="color:#BD93F9"> 2</span><span style="color:#FF79C6"> ?</span><span style="color:#F8F8F2"> split[</span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">] </span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> key </span><span style="color:#FF79C6">===</span><span style="color:#F8F8F2"> bearerKey;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> helloWorld </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#F8F8F2">    functions.https.</span><span style="color:#50FA7B">onRequest</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">request</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">response</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">      const</span><span style="color:#F8F8F2"> isValidBearer</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> validBearer</span><span style="color:#F8F8F2">(request);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">      if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">isValidBearer) {</span></span>
<span class="line"><span style="color:#F8F8F2">        response.</span><span style="color:#50FA7B">status</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">400</span><span style="color:#F8F8F2">).</span><span style="color:#50FA7B">json</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">            error</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">Not Authorized</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">      }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">      response.</span><span style="color:#50FA7B">json</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        result</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> `Hello World.`</span></span>
<span class="line"><span style="color:#F8F8F2">      });</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><hr />
<h3>Summary</h3>
<p>Firebase is so handy 😃. If this project or any other work I’m publishing as open source piece of software might interest you, follow me on <a href="https://twitter.com/daviddalbusco" rel="noopener noreferrer external">Twitter</a> and say hi. But more important:</p>
<p>Stay home, stay safe!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Takeover The Cordova Facebook Plugin Maintenance]]></title>
          <description><![CDATA[I am giving up being the active maintainer of the Cordova Facebook Plugin. Anyone willing to replace me?]]></description>
          <link>https://daviddalbusco.com/blog/takeover-the-cordova-facebook-plugin-maintenance/</link>
          <pubDate>Fri, 27 Mar 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*EPWjs7wN0Q2-8F2EeaiIqw.png" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@thoughtcatalog?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Thought Catalog</a> on <a href="https://unsplash.com/s/photos/facebook?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I share <a href="https://daviddalbusco.com/blog/how-to-call-the-service-worker-from-the-web-app-context" rel="noopener noreferrer external">one trick a day</a> until the end of the COVID-19 quarantine in Switzerland, April 19th 2020. <strong>Twenty-three</strong> days left until hopefully better days.</p>
<hr />
<p>It has been in my mind for a while now and I think that, even if this post is not really going to be trick, this series of article is the perfect time to announce that I am giving up being the active maintainer of the <a href="https://github.com/jeduan/cordova-plugin-facebook4" rel="noopener noreferrer external">Cordova Facebook Plugin</a>.</p>
<p>This entry will be a bit more metaphysical than usual, I will tell you my story, how and why I ended up being the maintainer and also present what I did as such (not that much actually).</p>
<p>But more <strong>important</strong>, I would rather like not to sunset my duties without anyone taking over the shift.</p>
<p>Therefore, if you are up to become the new active maintainer, <strong>ping me</strong> 🙏.</p>
<hr />
<h3>How Did I Become The Maintainer?</h3>
<p>Funny story, I have almost no clue of objective-C. I did have a Java background, that’s why I can understand and write Android code but the iOS part? Mostly interpretation and using spider-sense 😉.</p>
<p>You may ask yourself, but then, how come did you became the maintainer of a Cordova plugin?</p>
<p>Three or four years ago (times fly), after a corporate career as junior and then senior developer, business analyst, project manager, team leader, company manager etc. etc. I was looking for something new and decided to try to build my own “startup”. Nowadays I use the term “startup” with quotation marks because at the end of the day, I was just a guy alone trying to do his best.</p>
<p>This “startup” was <a href="https://fluster.io" rel="noopener noreferrer external">Fluster</a>, a simple search for roommates and flats platform which I created because finding a new place to live in Zürich freaking sucks (excuse my french) and which, by the way, I am also about to sunset really soon.</p>
<p>This platform was a mobile application developed with <a href="https://ionicframework.com" rel="noopener noreferrer external">Ionic</a> (early version 4) and <a href="https://cordova.apache.org" rel="noopener noreferrer external">Cordova</a> available in the <a href="https://itunes.apple.com/app/id1187266720" rel="noopener noreferrer external">App Store</a>, <a href="https://play.google.com/store/apps/details?id=io.fluster.fluster" rel="noopener noreferrer external">Google Play</a> and as a <a href="https://m.fluster.io" rel="noopener noreferrer external">Progressive Web Apps</a>.</p>
<p>To design its UX and develop it, I mostly decided to rely on the Facebook login and platform for two reasons:</p>
<ol>
<li>When it goes to mobile devices, I don’t like to enter data. I also don’t like when the onboarding takes to much steps and is slow</li>
<li>Back then, Tinder was really big</li>
</ol>
<p>After a year or something, I added a Google Login support too but most of the users were still using the Facebook one.</p>
<p>Long story short, if I remember correctly, there was one day a bug in the Cordova Facebook Plugin which could had only been solved by updating the Facebook SDK. <a href="https://twitter.com/jeduan" rel="noopener noreferrer external">Jeduan Cornejo</a>, the author of the plugin, was not interested anymore to maintains it, therefore I provided a pull request, he granted me as a maintainer, I merged my own pull request and that was it, it was the begin of my activities.</p>
<p>Looking back at the history of the <a href="https://github.com/jeduan/cordova-plugin-facebook4/blob/master/CHANGELOG.md" rel="noopener noreferrer external">CHANGELOG</a> I notice that it actually all began around two years ago, somewhere around March 24th 2018. So I’ve been a maintainer for two years.</p>
<hr />
<h3>Why I Gave Up?</h3>
<p>First of all, I gave up being a maintainer of this plugin because <strong>I don’t use it</strong> anymore and this since probably more than a year as I stopped being active in Fluster (the application, not the company) more than a year ago. It means that I remained the maintainer even without having any interest to it for quite a while. I don’t want to be praised for that or anything else I did regarding these activities, I’m just saying it out at loud to explain my motivation, don’t misunderstand me about this.</p>
<p>Furthermore, I don’t want to use it anymore. Since Fluster, I never ever implemented Facebook login again. I even don’t implement the option to share to this platform in all my personal recent work.</p>
<hr />
<h3>Facebook (The Company) Sucks</h3>
<p>Excuse my french again 😅. Most probably, almost no one nowadays is going to say that he/she likes Facebook. We are all aware that Facebook, the company, is evil. Between scandals, leaks, data breaches, data sold, etc. nothing can probably change this really soon. I say “probably” because for example in the 90s Microsoft used to be known as evil too but they managed to make good things to become respectable. Before them, IBM used to be seen as evil too but nowadays there are just…well there are just still alive, like always. IBM is going to be there somewhere selling super complicated solutions for ever 🤣.</p>
<p>But, regarding my above point on view on Facebook, here are two things you might not know:</p>
<hr />
<h4>Worst Communication Ever</h4>
<p>At the time all data breaches appeared in the news, Facebook began to chase their leaks in order to fix these, or maybe to hide more of these, who knows. From my external developer eyes who was using there API, I noticed that they did so because they began to ship updates really fast, so fast that sometimes they even deprecated services and only released the release notes informing you that these were down afterwards. I remember having seen things being stop and reading about it one day later.</p>
<p>They also did changes to their server infrastructure and sometimes stopped them for days without any prior information or any explanations afterwards.</p>
<p>I will never forget one particular incident. Suddenly it was not possible anymore to display users profile pictures. After tracking down the problem and spent several hours in the issue tracker of Facebook, I finally figured out that one issue was really close to mine. At some point, we were more than 500 people around the world following the exact same issue. During the night, Facebook decided to change the status of the issue from “open” to “in progress” and finally solved it around two days later. I never ever heard or read any explanations about what happened. I don’t even ask for apologies, I mean problems happens, but no comments, no messages, nothing, that's a pity.</p>
<p>Since that day I always tell to my self, sort of my private joke, that the communication of Facebook should be taught in schools as an example of how to communicate badly with developers.</p>
<hr />
<h4>Facebook Do Not Delete Ads Profile</h4>
<p>As much as I criticize Facebook, I am also, even more strongly, criticize my self.</p>
<p>When I developed Fluster I tried to reach my audience mostly with Facebook and Google ads. Probably because that’s what I learned in previous jobs. Concretely I invested of couple of thousand dollars of my private savings over several months to try to grow my business. Even if I was believing in it, I kind of feel bad about it. I am really not proud of my self to have tried to use ads, I mean, it’s ads, it is not cool and money can be use so much more wisely.</p>
<p>But at least the good point of this, is that I learned from what I consider as I a personal error and it probably helped me to become the person I’m today, or at least helped me to be more aware of what I want in life, what I want to achieve and more important, how.</p>
<p>That being said and here is my point. Even though my decision was bad, I should be able to delete my data. In Europe there is GDPR, which we don’t really have in Switzerland, but still, I should be able to request a deletion of my data right? Guess what, Facebook don’t wand and don’t allow deletion of Ads related profiles. No matter what, your data are going to stay there for ever.</p>
<p>Oh and by the way, Google is not better on that point, you cannot delete Google Ads profile neither.</p>
<hr />
<h3>Maintainer Duties</h3>
<p>Bad or good, the Facebook login is still used by a lot of applications, more than 6'000 downloads a week on <a href="https://www.npmjs.com/package/cordova-plugin-facebook4" rel="noopener noreferrer external">npm</a>, probably a lot of these are doing some good and it still needs a maintainer.</p>
<p>As such, I mostly did the following activities:</p>
<ul>
<li>Taking care of maintaining a clear issues tracker. Most the time, issues were actually support request, therefore I kindly asked developers to use other platform to handle these as otherwise it would have made the real issues really not trackable and not noticeable</li>
<li>Updating the documentation according developer inputs</li>
<li>Merging important Pull Requests</li>
<li>Updating the Facebook SDK</li>
<li>Providing a sample repo to help with the testing but more important, to help reproduce submitted issues</li>
</ul>
<p>Of course you will be able to organize yourself as you wish and I even now think that having a new active maintainer might bring some fresh ideas to the plugin.</p>
<hr />
<h3>Summary</h3>
<p>This opinionated blog post contained a lot of Facebook bashing, probably too mush, but facts don’t speak currently for this company. Hopefully they will be able in the future, as Microsoft and other did, to gain again a credibility, hopefully by giving their talented employees the opportunities to improve things which I am convinced they all are eager to change.</p>
<p>Stay home, stay safe!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Gatsby Tricks: Viewport, CSS Modules Transition And i18n Tricks]]></title>
          <description><![CDATA[Overriding Viewport settings, CSS modules transition and an i18n formatting tricks with Gatsby]]></description>
          <link>https://daviddalbusco.com/blog/gatsby-tricks-viewport-css-modules-transition-and-i18n-tricks/</link>
          <pubDate>Thu, 26 Mar 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*ytcX2rE89fOxjHjKfEAJng.png" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@massrecall?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Ronald Ladines</a> on <a href="https://unsplash.com/s/photos/free?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I share <a href="https://daviddalbusco.com/blog/how-to-call-the-service-worker-from-the-web-app-context" rel="noopener noreferrer external">one trick a day</a> until the end of the COVID-19 quarantine in Switzerland, April 19th 2020. <strong>Twenty-four</strong> days left until hopefully better days.</p>
<hr />
<p>Today I developed further the new website I’m currently building with <a href="https://www.gatsbyjs.org" rel="noopener noreferrer external">Gatsby</a> for a new side project we have with two friends. I discovered some new tricks, that’s why there are the inspiration for my today’s diary entry.</p>
<hr />
<h3>Overriding Viewport</h3>
<p>After a couple of hours of development, I had a basic website ready and even had developed the welcome page. I tested it across browsers and simulated different devices in Chrome, everything went fine until I tried out iPads.</p>
<p>I was a bit surprise because it did worked out everywhere else. As a skeptical person, of course my first reaction was “that’s probably a Chrome bug” (🤣), therefore I opened XCode and ran the same test on the simulator. To my surprise, the problem was still happening.</p>
<p>Concretely the following was happening.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*t7rBjEcQRqcbuy183JaSSw.gif" alt="" / loading="lazy"></p>
<p><em>Browser left ok vs iPad right not ok</em></p>
<p>It took me a bit to finally figure out what was the problem but finally my spider-sense kicked in and I had the feeling that the problem was linked to the specification of the <code>viewport</code> in the <code>head</code> of the page.</p>
<p>At first, I tried to solve the issue while injecting the meta information in the <code>seo.js</code> page using <a href="https://github.com/nfl/react-helmet" rel="noopener noreferrer external">React Helmet</a>. Unfortunately this didn’t worked out, as doing so, the page contained two meta information for the viewport and therefore mine was ignored. Finally, I found the solution in the Gatsby’s <a href="https://www.gatsbyjs.org/docs/custom-html/" rel="noopener noreferrer external">documentation</a>. To solve my issue I had to copy the default cached file <code>.cache/default-html.js</code> to a new file <code>src/html.js</code> . Doing so, on each build, Gatsby will use my local configuration instead of the default one. Once copied I just had to modify the viewport and my problem was gone 🥳.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">meta</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    name</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">viewport</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    content</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">/></span></span></code></pre><hr />
<h3>CSS Modules Transition</h3>
<p>My two colleagues, both designers, have always great design ideas. Often simple and effective but the more we collaborate together, the more we begin to add tiny animations to make everything cool 😎.</p>
<p>In this project I’m using <a href="https://github.com/css-modules/css-modules" rel="noopener noreferrer external">CSS modules</a> and to make some text appears, I had to use the property <code>transition</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD">    opacity</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">    visibility</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> hidden</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">    transition</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> opacity </span><span style="color:#BD93F9">0.25</span><span style="color:#FF79C6">s</span><span style="color:#BD93F9"> 0.8</span><span style="color:#FF79C6">s</span><span style="color:#BD93F9"> ease-in</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    &#x26;.animate {</span></span>
<span class="line"><span style="color:#8BE9FD">        opacity</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 1</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">        visibility</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> inherit</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>To my surprise, again, what a day, <code>transition</code> didn’t worked out at all. I quickly noticed that the reason behind that was the <code>opacity</code> was converted to a scoped CSS. To be honest, I don’t know exactly why 🤷‍♂️. Anyway I figured out that by forcing its importation without transforming the property it solved my issue.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">:global</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD">    transition</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> opacity </span><span style="color:#BD93F9">0.25</span><span style="color:#FF79C6">s</span><span style="color:#BD93F9"> 0.8</span><span style="color:#FF79C6">s</span><span style="color:#BD93F9"> ease-in</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>And speaking of this <code>transition</code> , did you now that second time parameter is the delay after which the transition should happens? The above can be read as: After 0.8 seconds, start a transition on the opacity property for 0.25s and do that slowly at the begin. Pretty cool 😁.</p>
<hr />
<h3>i18n formatting</h3>
<p>I shared some learnings regarding Gatsby and i18n in a previous blog post about <a href="https://daviddalbusco.com/blog/internationalization-with-gatsby" rel="noopener noreferrer external">internationalization</a> but today I had to use <a href="https://github.com/formatjs/react-intl" rel="noopener noreferrer external">React-intl</a> a bit more.</p>
<p>I notably learned how to style a text. For example, let’s say we want to display a title “hello <strong>world</strong>”.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#8BE9FD;font-style:italic">module</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">exports</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    intro</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">hello &#x3C;strong>world&#x3C;/strong></span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>We can either split it in two different translations or take advantages of the React-intl <code>FormattedMessage</code> component to style our text.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">FormattedMessage</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">        id</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">intro</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">        values</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#50FA7B">            strong</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">...</span><span style="color:#FFB86C;font-style:italic">chunks</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FF79C6">strong</span><span style="color:#F8F8F2">></span><span style="color:#FF79C6">{</span><span style="color:#F8F8F2">chunks</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">strong</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        }</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#F8F8F2">    /></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">></span></span></code></pre><hr />
<h3>Summary</h3>
<p>Nothing here which might send humanity to Mars but still, to me, small steps and small learning are rewarding too and these make my day. I hope this blog post was enjoyable to you and that it might help you in the future, who knows.</p>
<p>Stay home, stay safe!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Test Angular Pipes With Services]]></title>
          <description><![CDATA[How to test an Angular pipe which uses injected services]]></description>
          <link>https://daviddalbusco.com/blog/test-angular-pipes-with-services/</link>
          <pubDate>Wed, 25 Mar 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*q3KXO31t7qXMjn0bf5Z3OQ.png" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@guillaume_t?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Guillaume TECHER</a> on <a href="https://unsplash.com/s/photos/free?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I share <a href="https://daviddalbusco.com/blog/how-to-call-the-service-worker-from-the-web-app-context" rel="noopener noreferrer external">one trick a day</a> until the end of the COVID-19 quarantine in Switzerland, April 19th 2020. <strong>Twenty-five</strong> days left until hopefully better days.</p>
<hr />
<p>Today I spent much time deep focused at writing new <a href="https://angular.io" rel="noopener noreferrer external">Angular</a> components and their related unit tests, that I even missed this morning online “stand-up” and almost feel like I spend my day in some kind of vortex.</p>
<p>Anyway, I like this challenge, I don’t want to skip today’s blog post and I would like to share with you how I tested a new pipe I created. Moreover, I don’t pretend to be the champion of the exercise, therefore, if you notice anything which can be enhanced, ping me with your comments, I would be happy to improve my skills 🙏.</p>
<hr />
<h3>Create A Pipe</h3>
<p>Let’s first create a blank pipe called “filter” with the <code>ng</code> command line.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">ng</span><span style="color:#F1FA8C"> g</span><span style="color:#F1FA8C"> pipe</span><span style="color:#F1FA8C"> filter</span></span></code></pre><p>This creates a blank pipe like the following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Pipe, PipeTransform } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Pipe</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  name</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">filter</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> FilterPipe</span><span style="color:#FF79C6"> implements</span><span style="color:#8BE9FD;font-style:italic"> PipeTransform</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  transform</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">value</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> any</span><span style="color:#F8F8F2">, </span><span style="color:#FF79C6">...</span><span style="color:#FFB86C;font-style:italic">args</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> any</span><span style="color:#F8F8F2">[])</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> any</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9"> null</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>And it’s related test:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { FilterPipe } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./filter.pipe</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">describe</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">FilterPipe</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    it</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">create an instance</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> pipe </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> FilterPipe</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#50FA7B">        expect</span><span style="color:#F8F8F2">(pipe).</span><span style="color:#50FA7B">toBeTruthy</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><p>You can be or not an Angular fan but I think we can all be agree that it’s pretty cool to have a CLI which creates class and related test without any effort.</p>
<hr />
<h3>Create A Service</h3>
<p>As staten in my opening, the goal is to test a pipe which uses an injected service.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">ng</span><span style="color:#F1FA8C"> g</span><span style="color:#F1FA8C"> service</span><span style="color:#F1FA8C"> translation</span></span></code></pre><p>For demo purpose, we create this dummy service “translation” wich return not that much except either “Génial” or “Awesome” as an observable.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Injectable } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Observable, of } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">rxjs</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Injectable</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    providedIn</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">root</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> TranslationService</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    translate</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">lang</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Observable</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#50FA7B"> of</span><span style="color:#F8F8F2">(lang </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">fr</span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> ?</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">Génial</span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> :</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">Awesome</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>Implement Pipe</h3>
<p>Our service being ready, we use it to enhance our pipe.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Pipe, PipeTransform } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { TranslationService } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./translation.service</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Observable } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">rxjs</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Pipe</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    name</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">filter</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> FilterPipe</span><span style="color:#FF79C6"> implements</span><span style="color:#8BE9FD;font-style:italic"> PipeTransform</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    constructor</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">private</span><span style="color:#FFB86C;font-style:italic"> translationService</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> TranslationService</span><span style="color:#F8F8F2">) {}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    transform</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">lang</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Observable</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.translationService.</span><span style="color:#50FA7B">translate</span><span style="color:#F8F8F2">(lang);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Which by the way can be use with the help of the <code>async</code> pipe in a template (in following example, <code>lang</code> is a public string variable of the component)</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">textarea</span><span style="color:#50FA7B;font-style:italic"> [value]</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">lang | filter | async</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">textarea</span><span style="color:#F8F8F2">></span></span></code></pre><hr />
<h3>Update Pipe Test</h3>
<p>Locally I’m still able to run my test without errors but, because we are now injecting a service in our pipe, if we open the related unit test we notice a TypeScript error on the constructor <code>TS2554: expected 1 arguments, but got 0</code> . To fix this we have now to either inject the service or mock it.</p>
<hr />
<h4>Resolving Service In Test</h4>
<p>You can either resolve the service via the <code>inject</code> function or <code>TestBed</code> . Because the first solution didn’t worked out for me, the second one was my fallback.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { FilterPipe } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./filter.pipe</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { TestBed } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core/testing</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { TranslationService } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./translation.service</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">describe</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">FilterPipe</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">  beforeEach</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    TestBed</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">configureTestingModule</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        providers</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#F8F8F2">          TranslationService</span></span>
<span class="line"><span style="color:#F8F8F2">        ]</span></span>
<span class="line"><span style="color:#F8F8F2">      });</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  it</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">create an instance</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> service</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> TranslationService</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#F8F8F2">                              TestBed.</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(TranslationService);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> pipe </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> FilterPipe</span><span style="color:#F8F8F2">(service);</span></span>
<span class="line"><span style="color:#50FA7B">    expect</span><span style="color:#F8F8F2">(pipe).</span><span style="color:#50FA7B">toBeTruthy</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><hr />
<h4>Mock Service</h4>
<p>Another solution, the one I actually finally applied, is creating a mock of the service instead of providing it.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { FilterPipe } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./filter.pipe</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { of } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">rxjs</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { TranslationService } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./translation.service</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">describe</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">FilterPipe</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  let</span><span style="color:#F8F8F2"> translationServiceMock</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> TranslationService</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  beforeEach</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    translationServiceMock </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">      translate</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> jest.</span><span style="color:#50FA7B">fn</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">lang</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> of</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Awesome</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">))</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> any</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  it</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">create an instance</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> pipe </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> FilterPipe</span><span style="color:#F8F8F2">(translationServiceMock);</span></span>
<span class="line"><span style="color:#50FA7B">    expect</span><span style="color:#F8F8F2">(pipe).</span><span style="color:#50FA7B">toBeTruthy</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><hr />
<h3>Test Pipe Transform</h3>
<p>So far we were able to test that our pipe can be created even if it relies on a service but we are still not effectively testing its outcome. Therefore, here is the final piece, in which I use the mock of the service. Basically, once the pipe is created, we can access its <code>transform</code> method and proceed with some common testing.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { FilterPipe } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./filter.pipe</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { of } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">rxjs</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { take } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">rxjs/operators</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { TranslationService } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./translation.service</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">describe</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">FilterPipe</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  let</span><span style="color:#F8F8F2"> translationServiceMock</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> TranslationService</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  beforeEach</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    translationServiceMock </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">      translate</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> jest.</span><span style="color:#50FA7B">fn</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">lang</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> of</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Awesome</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">))</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> any</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  it</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">create an instance</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> pipe </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> FilterPipe</span><span style="color:#F8F8F2">(translationServiceMock);</span></span>
<span class="line"><span style="color:#50FA7B">    expect</span><span style="color:#F8F8F2">(pipe).</span><span style="color:#50FA7B">toBeTruthy</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  it</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">should translate</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> pipe </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> FilterPipe</span><span style="color:#F8F8F2">(translationServiceMock);</span></span>
<span class="line"><span style="color:#F8F8F2">    pipe.</span><span style="color:#50FA7B">transform</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">en</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">pipe</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">take</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">))</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">subscribe</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">text</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">        expect</span><span style="color:#F8F8F2">(text).not.</span><span style="color:#50FA7B">toBe</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">null</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#50FA7B">        expect</span><span style="color:#F8F8F2">(text).</span><span style="color:#50FA7B">toEqual</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Awesome</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">      });</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><hr />
<h3>Summary</h3>
<p>It still takes me a bit to find the right testing setting for projects, specially when they are new, but as soon as everything is in place as soon as I can access the <code>nativeElement</code> to perform queries, as I would do in a Web Component, I feel more comfortable and it began to be fun 😁.</p>
<p>Stay home, stay safe!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Starting In A New Company? Think Npmrc And Git Name]]></title>
          <description><![CDATA[How to configure your company's npm Enterprise registry and provide your name for Git interactions]]></description>
          <link>https://daviddalbusco.com/blog/starting-in-a-new-company-think-npmrc-and-git-name/</link>
          <pubDate>Tue, 24 Mar 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*HG7Ivs6JrMErB7GdQZWSog.png" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@fivepointseven?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Max Rovensky</a> on <a href="https://unsplash.com/s/photos/free?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I share <a href="https://daviddalbusco.com/blog/how-to-call-the-service-worker-from-the-web-app-context" rel="noopener noreferrer external">one trick a day</a> until the end of the COVID-19 quarantine in Switzerland, April 19th 2020. <strong>Twenty-six</strong> days left until hopefully better days.</p>
<hr />
<p>Every single time I am hired to join a team as an external developer for a while, I can guarantee you that I will have to google how I can configure the company <a href="https://www.npmjs.com" rel="noopener noreferrer external">npm</a> Enterprise registry on my laptop 🙈. Moreover, it is also pretty sure that my first commit is going to happens with my GitHub username <a href="https://github.com/peterpeterparker" rel="noopener noreferrer external">peterpeterparker</a> instead of my real name or any other expected IDs 😄.</p>
<p>Therefore see this new article as an open letter to my future self 😉.</p>
<hr />
<h3>Setting your company’s npm Enterprise registry</h3>
<p>If the company has hired you, there is a good chance that, unfortunately, at least some of their work is only available as closed source and distributed with a npm private registry. To configure such an entry, you can run the following command line in a terminal.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> config</span><span style="color:#F1FA8C"> set</span><span style="color:#F1FA8C"> registry</span></span></code></pre><h4>Npmrc</h4>
<p>If like me you are using your own laptop, you might not want to modify or mix up your private configuration with you client configuration right?</p>
<p>That’s why <a href="https://docs.npmjs.com/configuring-npm/npmrc.html" rel="noopener noreferrer external">npmrc</a> is there for us. With its help we are going to be able to define and switch between multiple npm profiles.</p>
<p>If you have not yet installed it, let’s first do so.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> i</span><span style="color:#F1FA8C"> npmrc</span><span style="color:#BD93F9"> -g</span></span></code></pre><p>Once installed, we can use it to create Enterprise profile. For example, let’s create a profile called “client”.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npmrc</span><span style="color:#BD93F9"> -c</span><span style="color:#F1FA8C"> client</span></span></code></pre><p>After the profile has been created, it becomes the one in use, therefore, if we run the registry command, it will now set the registry only for the active profile.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> config</span><span style="color:#F1FA8C"> set</span><span style="color:#F1FA8C"> registry</span></span></code></pre><h4>Switch Between Profiles</h4>
<p>Cool we now in our “client” profile, but how do we go back to the previous one, the default one? Nothing easier, run the <code>npmrc</code> command followed by the profile’s name.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npmrc</span><span style="color:#F1FA8C"> default</span></span></code></pre><p>Or if you want to switch back to the “client” one.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npmrc</span><span style="color:#F1FA8C"> client</span></span></code></pre><p>If you are lost and don’t know which profile is currently active, typing <code>npmrc</code> without any arguments will list the available profiles.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npmrc</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">Available</span><span style="color:#F1FA8C"> npmrcs:</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">*</span><span style="color:#F8F8F2"> default</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">client</span></span></code></pre><h4>Manual Configuration</h4>
<p>You might like command lines but you may also rather like to modify manually your configuration using <code>vi</code> for example 😉.</p>
<p>On a Mac, the profiles find place in a folder <code>.npmrcs</code> added to your user directory. When you switch between these, <code>npmrc</code> is going to change the symlink at the root of your profile.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">ls</span><span style="color:#F1FA8C"> /Users/daviddalbusco/.npmrcs/default</span></span>
<span class="line"><span style="color:#50FA7B">/Users/daviddalbusco/.npmrcs/default</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">ls</span><span style="color:#F1FA8C"> /Users/daviddalbusco/.npmrcs/client</span></span>
<span class="line"><span style="color:#50FA7B">/Users/daviddalbusco/.npmrcs/client</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">ls</span><span style="color:#BD93F9"> -ltr</span><span style="color:#F1FA8C"> /Users/daviddalbusco/.npmrc</span></span>
<span class="line"><span style="color:#50FA7B">/Users/daviddalbusco/.npmrc</span><span style="color:#F8F8F2"> -</span><span style="color:#FF79C6">></span><span style="color:#F1FA8C"> /Users/daviddalbusco/.npmrcs/default</span></span></code></pre><hr />
<h3>Setting Your Name In Git</h3>
<p>To specify another name and email for your Git activity, you can either edit your global <code>config</code> or proceed by repository, up to you. I personally rather like to proceed by projects because I’ve got many clients.</p>
<p>Therefore, if like me you want to use your real name instead of your super cool GitHub username and also specify the email your client assigned to you, you can edit the file <code>.git/config</code> which find places, normally, in any Git project. In this data, add the following information:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">[user]</span></span>
<span class="line"><span style="color:#50FA7B">    email</span><span style="color:#F1FA8C"> =</span><span style="color:#F1FA8C"> david.dalbusco@company.com</span></span>
<span class="line"><span style="color:#50FA7B">    name</span><span style="color:#F1FA8C"> =</span><span style="color:#F1FA8C"> David</span><span style="color:#F1FA8C"> Dal</span><span style="color:#F1FA8C"> Busco</span></span></code></pre><p>And that’s it, all the interactions with the specific repo are going to be identified with these information.</p>
<p>Note that if you rather like to specify other inputs on a global level, you can run the following command lines.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">git</span><span style="color:#F1FA8C"> config</span><span style="color:#BD93F9"> --global</span><span style="color:#F1FA8C"> user.name</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">David Dal Busco</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B">git</span><span style="color:#F1FA8C"> config</span><span style="color:#BD93F9"> --global</span><span style="color:#F1FA8C"> user.email</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">david.dalbusco@company.com</span><span style="color:#E9F284">"</span></span></code></pre><p>Finally, in case you would have already performed a commit and are looking to amend your last commit user name, you can proceed with the <code>--amend</code> option of Git.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">git</span><span style="color:#F1FA8C"> commit</span><span style="color:#BD93F9"> --amend</span><span style="color:#BD93F9"> --author=”David</span><span style="color:#F1FA8C"> Dal</span><span style="color:#F1FA8C"> Busco</span><span style="color:#FF79C6"> &#x3C;</span><span style="color:#F1FA8C">david.dalbusco@company.co</span><span style="color:#F8F8F2">m</span><span style="color:#FF79C6">></span><span style="color:#F1FA8C">”</span></span></code></pre><hr />
<h3>Summary</h3>
<p>Well I am pretty sure that even if I wrote the above lines, I may forget again in the future how I have to proceed. But at least this time, I will just have to browse my blog history 😇.</p>
<p>Stay home, stay safe!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Deploy Apps And Functions To Firebase From A Mono Repo With GitHub Actions]]></title>
          <description><![CDATA[How to deploy applications and functions from a mono repo with GitHub Actions to Firebase Hosting and Functions]]></description>
          <link>https://daviddalbusco.com/blog/deploy-apps-and-functions-to-firebase-from-a-mono-repo-with-github-actions/</link>
          <pubDate>Mon, 23 Mar 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*-sJcIhzFM8ypB7ifPgYdow.png" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@zhangzui?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">张 嘴</a> on <a href="https://unsplash.com/s/photos/free?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I share <a href="https://daviddalbusco.com/blog/how-to-call-the-service-worker-from-the-web-app-context" rel="noopener noreferrer external">one trick a day</a> until the end of the COVID-19 quarantine in Switzerland, April 19th 2020. <strong>Twenty-seven</strong> days left until hopefully better days.</p>
<hr />
<p>I am a big fan of a <a href="https://julienrenaux.fr/2019/11/25/building-deploying-stenciljs-apps-firebase-hosting-github-actions/" rel="noopener noreferrer external">blog post</a> published by <a href="https://twitter.com/julienrenaux" rel="noopener noreferrer external">Julien Renaux</a> a couple of months ago in which he displays how to deploy an application using <a href="https://github.com/features/actions" rel="noopener noreferrer external">GitHub Actions</a> to <a href="https://firebase.google.com/docs/hosting" rel="noopener noreferrer external">Firebase Hosting</a>.</p>
<p>The article is super comprehensive and I even already have published a <a href="https://daviddalbusco.com/blog/how-to-keep-secret-your-font-awesome-pro-token-in-public-github-actions" rel="noopener noreferrer external">following post</a> about it once before 🤣. Nevertheless I think that this challenge is the perfect excuse to publish again another follow-up 😉.</p>
<hr />
<h3>Mono Repo</h3>
<p>Our open source project <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a> contains many Progressive Web Apps and Cloud Functions, for which, obviously, I did set up GitHub actions as Julien displayed.</p>
<p>In this blog post I share the enhancement I had to implement in order to make the integration supports a mono repo.</p>
<hr />
<h3>To Firebase Hosting</h3>
<p>Let’s say that one of your application is available in a sub-folder of our repo called <code>docs</code> . Basically, everything you have to do in addition to the original post is to prefix all steps of the Action with your sub-directory, respectively with <code>docs</code> .</p>
<p>Concretely, if for example we would like to trigger the action when a merge happens in the master branch, but, only if a modifications impacts our specific app, we specify a path to the Action <code>on</code> listener.</p>
<p>Listen to push to master for the all repo:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#BD93F9">on</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">  push</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">    branches</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#FF79C6">      -</span><span style="color:#F1FA8C"> master</span></span></code></pre><p>Listen to push to master only if sub-folder is affected:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#BD93F9">on</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">  push</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">    branches</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#FF79C6">      -</span><span style="color:#F1FA8C"> master</span></span>
<span class="line"><span style="color:#8BE9FD">    paths</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#FF79C6">      -</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">docs/**</span><span style="color:#E9F284">"</span></span></code></pre><p>Because the Git checkout of our pipeline happens on the root level of our repo, when it goes to installing dependencies or running a build, we do have to observe our sub-folder too. For such purpose, GitHub Action provides a handy option <code>working-directory</code> for the <code>npm</code> steps.</p>
<p>Run <code>npm</code> in the root folder:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">-</span><span style="color:#8BE9FD"> name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> Install Dependencies</span></span>
<span class="line"><span style="color:#8BE9FD">        run</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> npm ci</span></span></code></pre><p>Run <code>npm</code> in a sub-directory:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">-</span><span style="color:#8BE9FD"> name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> Install Dependencies</span></span>
<span class="line"><span style="color:#8BE9FD">  run</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> npm ci</span></span>
<span class="line"><span style="color:#8BE9FD">  working-directory</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> ./docs</span></span></code></pre><p>Likewise, when it goes to artifacts, we also have to prefix the path to our bundle.</p>
<p>Archiving without sub-folder:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">-</span><span style="color:#8BE9FD"> name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> Archive Artifact</span></span>
<span class="line"><span style="color:#8BE9FD">  uses</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> actions/upload-artifact@master</span></span>
<span class="line"><span style="color:#8BE9FD">  with</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">    name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> www</span></span>
<span class="line"><span style="color:#8BE9FD">    path</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> www</span></span></code></pre><p>Archiving with sub-folder:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">-</span><span style="color:#8BE9FD"> name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> Archive Artifact</span></span>
<span class="line"><span style="color:#8BE9FD">  uses</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> actions/upload-artifact@master</span></span>
<span class="line"><span style="color:#8BE9FD">  with</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">    name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> docs</span></span>
<span class="line"><span style="color:#8BE9FD">    path</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> docs/www</span></span></code></pre><p>Finally, the Firebase Action does also provide an option to specify a project path.</p>
<p>Deploy to Firebase from root:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">-</span><span style="color:#8BE9FD"> name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> Deploy to Firebase</span></span>
<span class="line"><span style="color:#8BE9FD">  uses</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> w9jds/firebase-action@master</span></span>
<span class="line"><span style="color:#8BE9FD">  with</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">    args</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> deploy --only hosting</span></span>
<span class="line"><span style="color:#8BE9FD">  env</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">    FIREBASE_TOKEN</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> ${{ secrets.FIREBASE_TOKEN }}</span></span></code></pre><p>Deploy to Firebase for a sub-directory:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">-</span><span style="color:#8BE9FD"> name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> Deploy to Firebase</span></span>
<span class="line"><span style="color:#8BE9FD">  uses</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> w9jds/firebase-action@master</span></span>
<span class="line"><span style="color:#8BE9FD">  with</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">    args</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> deploy --only hosting</span></span>
<span class="line"><span style="color:#8BE9FD">  env</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">    FIREBASE_TOKEN</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> ${{ secrets.FIREBASE_TOKEN }}</span></span>
<span class="line"><span style="color:#8BE9FD">    PROJECT_ID</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">default</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#8BE9FD">    PROJECT_PATH</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./docs</span><span style="color:#E9F284">"</span></span></code></pre><p>All in all, here’s the action to deploy our application for a sub-folder of our<br />
mono repo to Firebase Hosting:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#8BE9FD">name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> CI - Docs</span></span>
<span class="line"></span>
<span class="line"><span style="color:#BD93F9">on</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">  push</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">    branches</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#FF79C6">      -</span><span style="color:#F1FA8C"> master</span></span>
<span class="line"><span style="color:#8BE9FD">    paths</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#FF79C6">      -</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">docs/**</span><span style="color:#E9F284">"</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">jobs</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">  build</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">    name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> Build</span></span>
<span class="line"><span style="color:#8BE9FD">    runs-on</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> ubuntu-latest</span></span>
<span class="line"><span style="color:#8BE9FD">    steps</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#FF79C6">      -</span><span style="color:#8BE9FD"> name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> Checkout Repo</span></span>
<span class="line"><span style="color:#8BE9FD">        uses</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> actions/checkout@master</span></span>
<span class="line"><span style="color:#FF79C6">      -</span><span style="color:#8BE9FD"> name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> Install Dependencies</span></span>
<span class="line"><span style="color:#8BE9FD">        run</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> npm ci</span></span>
<span class="line"><span style="color:#8BE9FD">        working-directory</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> ./docs</span></span>
<span class="line"><span style="color:#FF79C6">      -</span><span style="color:#8BE9FD"> name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> Build</span></span>
<span class="line"><span style="color:#8BE9FD">        run</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> npm run build</span></span>
<span class="line"><span style="color:#8BE9FD">        working-directory</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> ./docs</span></span>
<span class="line"><span style="color:#FF79C6">      -</span><span style="color:#8BE9FD"> name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> Archive Artifact</span></span>
<span class="line"><span style="color:#8BE9FD">        uses</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> actions/upload-artifact@master</span></span>
<span class="line"><span style="color:#8BE9FD">        with</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">          name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> docs</span></span>
<span class="line"><span style="color:#8BE9FD">          path</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> docs/www</span></span>
<span class="line"><span style="color:#8BE9FD">  deploy</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">    name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> Deploy</span></span>
<span class="line"><span style="color:#8BE9FD">    needs</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> build</span></span>
<span class="line"><span style="color:#8BE9FD">    runs-on</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> ubuntu-latest</span></span>
<span class="line"><span style="color:#8BE9FD">    steps</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#FF79C6">      -</span><span style="color:#8BE9FD"> name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> Checkout Repo</span></span>
<span class="line"><span style="color:#8BE9FD">        uses</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> actions/checkout@master</span></span>
<span class="line"><span style="color:#FF79C6">      -</span><span style="color:#8BE9FD"> name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> Download Artifact</span></span>
<span class="line"><span style="color:#8BE9FD">        uses</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> actions/download-artifact@master</span></span>
<span class="line"><span style="color:#8BE9FD">        with</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">          name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> docs</span></span>
<span class="line"><span style="color:#8BE9FD">          path</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> docs/www</span></span>
<span class="line"><span style="color:#FF79C6">      -</span><span style="color:#8BE9FD"> name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> Deploy to Firebase</span></span>
<span class="line"><span style="color:#8BE9FD">        uses</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> w9jds/firebase-action@master</span></span>
<span class="line"><span style="color:#8BE9FD">        with</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">          args</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> deploy --only hosting</span></span>
<span class="line"><span style="color:#8BE9FD">        env</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">          FIREBASE_TOKEN</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> ${{ secrets.FIREBASE_TOKEN }}</span></span>
<span class="line"><span style="color:#8BE9FD">          PROJECT_ID</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">default</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#8BE9FD">          PROJECT_PATH</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./docs</span><span style="color:#E9F284">"</span></span></code></pre><hr />
<h3>To Firebase Cloud</h3>
<p>In <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a> we are also taking advantages of the amazing <a href="https://firebase.google.com/docs/functions" rel="noopener noreferrer external">Cloud Functions</a> features of Firebase.</p>
<p>The process is basically the same as previously with that difference that you don’t have to build or even bundle anything because these steps are contained in the deployment process it self. Therefore checking out the repo, installing the dependencies and deploying is going to do the trick.</p>
<p>Note that again, as we have a mono repo, the following example happens in a sub-directory called <code>cloud</code> which contains the functions.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#8BE9FD">name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> CI - Cloud</span></span>
<span class="line"></span>
<span class="line"><span style="color:#BD93F9">on</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">  push</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">    branches</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#FF79C6">      -</span><span style="color:#F1FA8C"> master</span></span>
<span class="line"><span style="color:#8BE9FD">    paths</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#FF79C6">      -</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">cloud/**</span><span style="color:#E9F284">"</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD">jobs</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">  deploy</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">    name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> Deploy</span></span>
<span class="line"><span style="color:#8BE9FD">    runs-on</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> ubuntu-latest</span></span>
<span class="line"><span style="color:#8BE9FD">    steps</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#FF79C6">      -</span><span style="color:#8BE9FD"> name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> Checkout Repo</span></span>
<span class="line"><span style="color:#8BE9FD">        uses</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> actions/checkout@master</span></span>
<span class="line"><span style="color:#FF79C6">      -</span><span style="color:#8BE9FD"> name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> Install Dependencies</span></span>
<span class="line"><span style="color:#8BE9FD">        run</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> npm ci</span></span>
<span class="line"><span style="color:#8BE9FD">        working-directory</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> ./cloud/functions</span></span>
<span class="line"><span style="color:#FF79C6">      -</span><span style="color:#8BE9FD"> name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> Deploy to Firebase</span></span>
<span class="line"><span style="color:#8BE9FD">        uses</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> w9jds/firebase-action@master</span></span>
<span class="line"><span style="color:#8BE9FD">        with</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">          args</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> deploy --only functions</span></span>
<span class="line"><span style="color:#8BE9FD">        env</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">          FIREBASE_TOKEN</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> ${{ secrets.FIREBASE_TOKEN }}</span></span>
<span class="line"><span style="color:#8BE9FD">          PROJECT_ID</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">default</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#8BE9FD">          PROJECT_PATH</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./cloud</span><span style="color:#E9F284">"</span></span></code></pre><hr />
<h3>Summary</h3>
<p>GitHub actions are awesome! Seriously, few lines, few configuration, supports mono repo, really a perfect solution for a project like ours.</p>
<p>That being said, notably if you are and want to use them in your company, you might be interested by another blog post published by Julien which describes some <a href="https://julienrenaux.fr/2019/12/20/github-actions-security-risk/" rel="noopener noreferrer external">possible risks</a> linked to versioning and dependencies.</p>
<p>You are also most welcomed to have a look to our open source Actions in our <a href="https://github.com/deckgo/deckdeckgo" rel="noopener noreferrer external">repo</a> or even better, if you notice anything which can be improved in <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>, don’t hesitate to open an issue or even better, send us a Pull Request 😃.</p>
<p>Stay home, stay safe!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Get App Name And Version In Angular]]></title>
          <description><![CDATA[How to read or display the name and version of your application in Angular without duplicating these information]]></description>
          <link>https://daviddalbusco.com/blog/get-app-name-and-version-in-angular/</link>
          <pubDate>Sun, 22 Mar 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*KwiK0jtPVZnLUyZV1BPdag.png" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@joechau?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Joe Chau</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I share <a href="https://daviddalbusco.com/blog/how-to-call-the-service-worker-from-the-web-app-context" rel="noopener noreferrer external">one trick a day</a> until the end of the COVID-19 quarantine in Switzerland, April 19th 2020. <strong>Twenty-height</strong> days left until hopefully better days.</p>
<hr />
<p>Today, Sunday, I had to upgrade an application I’ve developed last year with <a href="https://ionicframework.com" rel="noopener noreferrer external">Ionic</a> and <a href="https://angular.io" rel="noopener noreferrer external">Angular</a> because my client seems to finally have decided to publish it in stores (better start without technical debt 😉).</p>
<p>I’ve to say, the update process to Angular v9 and Ionic v5 went just smoothly. It took me a bit more time to upgrade the <a href="https://cordova.apache.org" rel="noopener noreferrer external">Cordova</a> plugins, specially the one dedicated to Firebase, but fortunately I found the solution shared by his maintainer, <a href="https://twitter.com/dpa99c" rel="noopener noreferrer external">Dave Alden</a>, who always does an outstanding job.</p>
<p>Once done, I was still facing one last issue. I didn’t touched the app in months and meanwhile, Google Analytics had been really deprecated and I was not able anymore to build my platform because the corresponding plugin was outdated.</p>
<p>Instead of finding the solution, I just removed my old GA implementation and migrated to the new Firebase Analytics implementation in less time than expected.</p>
<p>For the web, I’m using <a href="https://github.com/angular/angularfire" rel="noopener noreferrer external">AngularFire</a>, which offers an option to track deployments with application’s name and versions.</p>
<p>Therefore here is how you can get or read the name and version of your application in Angular.</p>
<hr />
<h3>Import JSON File In TypeScript</h3>
<p>We don’t want to duplicate the app’s name or version, that’s why I suggest that we read these from our <code>package.json</code> . In order to import JSON files in TypeScript, we need to instruct the compiler to accept such type of data. To do so, in our <code>tsonfig.json</code> we turn the option <code>resolveJsonModule</code> to <code>true</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#E9F284">"</span><span style="color:#F1FA8C">compilerOptions</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">: {</span></span>
<span class="line"><span style="color:#8BE9FE">  "</span><span style="color:#8BE9FD">resolveJsonModule</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span></span>
<span class="line"><span style="color:#F8F8F2">},</span></span></code></pre><hr />
<h3>Environment Variables</h3>
<p>A convenient way to handle environment variables is possible, out of the box, in Angular through the use of the multiples <code>environment.ts</code> files. That’s why we enhance these to “inject” our application’s name and version.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { name, version } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">../../package.json</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> environment </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    production</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    name,</span></span>
<span class="line"><span style="color:#F8F8F2">    version</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>That’s it, in our code we have now access the information 😁.</p>
<hr />
<h3>AngularFire Analytics</h3>
<p>If like me, you use AngularFire and would like to track these information, proceed as the following in your <code>app.module.ts</code> . Note that in the spinet I also set <code>anonymize_ip</code> to <code>true</code> , as it should, in my humble opinion, always be the case.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { AngularFireAnalyticsModule, APP_NAME, APP_VERSION, CONFIG } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@angular/fire/analytics</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { environment } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">../environments/environment</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">NgModule</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    providers</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#F8F8F2">        {</span></span>
<span class="line"><span style="color:#F8F8F2">            provide</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> CONFIG,</span></span>
<span class="line"><span style="color:#F8F8F2">            useValue</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">                allow_ad_personalization_signals</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">                anonymize_ip</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"><span style="color:#F8F8F2">        },</span></span>
<span class="line"><span style="color:#F8F8F2">        { provide</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> APP_NAME, useValue</span><span style="color:#FF79C6">:</span><span style="color:#50FA7B"> environment</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B;font-style:italic">name</span><span style="color:#F8F8F2"> },</span></span>
<span class="line"><span style="color:#F8F8F2">        { provide</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> APP_VERSION, useValue</span><span style="color:#FF79C6">:</span><span style="color:#50FA7B"> environment</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B;font-style:italic">version</span><span style="color:#F8F8F2"> }</span></span>
<span class="line"><span style="color:#F8F8F2">    ]</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> AppModule</span><span style="color:#F8F8F2"> {}</span></span></code></pre><hr />
<h3>Summary</h3>
<p>I’m agree, this isn’t the deepest and longest blog post I have ever written but I hope it might be useful anyway to someone in the future, one never knows 😄.</p>
<p>Stay home, stay safe!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[How To Declare And Use Ionic Modals With Stencil]]></title>
          <description><![CDATA[How to declare and use Ionic modals with Stencil incl. passing parameters, close actions and hardware back button support]]></description>
          <link>https://daviddalbusco.com/blog/how-to-declare-and-use-ionic-modals-with-stencil/</link>
          <pubDate>Sat, 21 Mar 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*MUxo-oCL6aB0QEjOH8AU3A.png" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@tirzavandijk?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Tirza van Dijk</a> on <a href="https://unsplash.com/s/photos/free?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I am sharing <a href="https://medium.com/@david.dalbusco/one-trick-a-day-d-34-469a0336a07e" rel="noopener noreferrer external">one trick a day</a> until the end of the COVID-19 quarantine in Switzerland, April 19th 2020. <strong>Twenty-nine</strong> days left until hopefully better days.</p>
<hr />
<p>This week on Slack, we discussed the usage of <a href="https://ionicframework.com" rel="noopener noreferrer external">Ionic</a> modals in <a href="https://stenciljs.com" rel="noopener noreferrer external">Stencil</a> apps. I shared the solution we have implemented in all applications of <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>, our open source editor for presentations, and it seemed to do the trick.</p>
<p>Even though the related Ionic Modal documentation is self explanatory and really well documented, when it comes to vanilla JavaScript or modern frontend frameworks, there isn’t any information regarding the Stencil usage.</p>
<p>That’s why, all in all, I thought I can share an article about this particular subject.</p>
<hr />
<h3>Controllers</h3>
<p>Probably the major difference in terms of usage, if you compare to Ionic for React or vanilla Javascript, using Ionic modals in Stencil requires <strong>controllers</strong>.</p>
<p>For having tested all flavors (except Vue) of Ionic, this is still my favorite solution because I feel more comfortable with, but I’m not sure it will remains like this in the future as, if I understand correctly, many developers including some of the team itself rather like the other solution, without controllers. Therefore, if you read this article in a late future, check first if these still exists or not 😉.</p>
<hr />
<h3>Create A Modal</h3>
<p>The modal itself is nothing else than a component. That’s why, if we want to add one to our application, we first create a new component which I rather like to not set as shadowed. Doing so, it will be possible to inherit the CSS properties and style of the application easily.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Component, Element, h} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@stencil/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  tag</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-modal</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> AppRemoteConnect</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  @</span><span style="color:#50FA7B">Element</span><span style="color:#F8F8F2">() el</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLElement</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-content</span><span style="color:#50FA7B;font-style:italic"> class</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ion-padding</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Hello&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ion-content</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    ]</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>Open A Modal</h3>
<p>As staten in the introduction, to use modals with Stencil, we are going to use controllers. The trick is to pass the modal tag name as value of the <code>component</code> variable.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Component, h} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@stencil/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {modalController} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@ionic/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  tag</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-home</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  styleUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-home.css</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> AppHome</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#FF79C6"> async</span><span style="color:#50FA7B"> openModal</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> modal</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLIonModalElement</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#FF79C6">                 await</span><span style="color:#F8F8F2"> modalController.</span><span style="color:#50FA7B">create</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">      component</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-modal</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#F8F8F2"> modal.</span><span style="color:#50FA7B">present</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-content</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-button</span><span style="color:#50FA7B;font-style:italic"> onClick</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">openModal</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                    color</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">primary</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">          &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-label</span><span style="color:#F8F8F2">>Open Modal&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ion-label</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ion-button</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ion-content</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>If everything went according plan, once started and opened, it should looks like the following:</p>
<p><img src="https://daviddalbusco.com/assets/images/1*oi7_UdlrP21Wuwm5UyB4mw.png" alt="" / loading="lazy"></p>
<hr />
<h3>Close A Modal</h3>
<p>In this chapter we are going to explore the different ways to close the modal.</p>
<h4>Button In Modal Header</h4>
<p>To close the modal from itself, we use the document reference to find the closest <code>ion-modal</code> element in order to call the method <code>dismiss</code> which is exposed to achieve such a goal.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Component, Element, h} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@stencil/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  tag</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-modal</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> AppRemoteConnect</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  @</span><span style="color:#50FA7B">Element</span><span style="color:#F8F8F2">() el</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLElement</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  async</span><span style="color:#50FA7B"> closeModal</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#F8F8F2"> (</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.el.</span><span style="color:#50FA7B">closest</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">ion-modal</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">as</span></span>
<span class="line"><span style="color:#F8F8F2">           HTMLIonModalElement).</span><span style="color:#50FA7B">dismiss</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-header</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-toolbar</span><span style="color:#50FA7B;font-style:italic"> color</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">secondary</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">          &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-buttons</span><span style="color:#50FA7B;font-style:italic"> slot</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">start</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-button</span><span style="color:#50FA7B;font-style:italic"> onClick</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">closeModal</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">              &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-icon</span><span style="color:#50FA7B;font-style:italic"> name</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">close</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ion-icon</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ion-button</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">          &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ion-buttons</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ion-toolbar</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ion-header</span><span style="color:#F8F8F2">>,</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-content</span><span style="color:#50FA7B;font-style:italic"> class</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ion-padding</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Hello&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ion-content</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    ]</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Again, if everything went fine, a close button in the header should now be displayed.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*ZV2OKs1bzf6eqI0SJnR9jg.png" alt="" / loading="lazy"></p>
<hr />
<h4>Hardware Back Button Support</h4>
<p>It’s been a while since I didn’t tested the hardware back button support to close the modal on Android but what I generally do is adding a navigation listener, in the modal, which call the same close function as the one we defined before. This hack is based on the history, that’s why a state has to be pushed when the modal is loaded.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Listen} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@stencil/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">async </span><span style="color:#50FA7B">componentDidLoad</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#F8F8F2">  history.</span><span style="color:#50FA7B">pushState</span><span style="color:#F8F8F2">({modal</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">}, </span><span style="color:#BD93F9">null</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Listen</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">popstate</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, {target</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">window</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#F8F8F2">async </span><span style="color:#50FA7B">handleHardwareBackButton</span><span style="color:#F8F8F2">(_e: PopStateEvent) {</span></span>
<span class="line"><span style="color:#FF79C6">  await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">closeModal</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h4>Backdrop Dismiss</h4>
<p>Per default, modals can be dismissed through a click on their backdrops. If you wish to disable this option, you have to specify it when at the controller level.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> modal</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLIonModalElement</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> modalController.</span><span style="color:#50FA7B">create</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  component</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-modal</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  backdropDismiss</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> false</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><hr />
<h3>Passing Parameters</h3>
<p>In this chapter we are passing parameters from the page to the modal and in the other direction.</p>
<h4>Page To Modal</h4>
<p>This is probably my favorite thing across all flavors of Ionic modals I tried. Passing parameters with Stencil is super duper easy.</p>
<p>To read parameters in the modals, we only have to define properties (<code>@Prop()</code>).</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Component, Element, h, Listen, Prop} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@stencil/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  tag</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-modal</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> AppRemoteConnect</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  @</span><span style="color:#50FA7B">Element</span><span style="color:#F8F8F2">() el</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLElement</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  @</span><span style="color:#50FA7B">Prop</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">  greetings</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  @</span><span style="color:#50FA7B">Listen</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">popstate</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, {target</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">window</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">  async</span><span style="color:#50FA7B"> handleHardwareBackButton</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">_e</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> PopStateEvent</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">closeModal</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  async</span><span style="color:#50FA7B"> closeModal</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#F8F8F2"> (</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.el.</span><span style="color:#50FA7B">closest</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">ion-modal</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#FF79C6">           as</span><span style="color:#8BE9FD;font-style:italic"> HTMLIonModalElement</span><span style="color:#F8F8F2">).</span><span style="color:#50FA7B">dismiss</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-header</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-toolbar</span><span style="color:#50FA7B;font-style:italic"> color</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">secondary</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">          &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-buttons</span><span style="color:#50FA7B;font-style:italic"> slot</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">start</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-button</span><span style="color:#50FA7B;font-style:italic"> onClick</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">closeModal</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">              &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-icon</span><span style="color:#50FA7B;font-style:italic"> name</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">close</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ion-icon</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ion-button</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">          &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ion-buttons</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ion-toolbar</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ion-header</span><span style="color:#F8F8F2">>,</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-content</span><span style="color:#50FA7B;font-style:italic"> class</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ion-padding</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#FF79C6">           {</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.greetings</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ion-content</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    ]</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Which we then just pass through the controllers.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">private async </span><span style="color:#50FA7B">openModal</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> modal</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLIonModalElement</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> modalController.</span><span style="color:#50FA7B">create</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    component</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-modal</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    backdropDismiss</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    componentProps</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">      greetings</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">Yolo</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  await</span><span style="color:#F8F8F2"> modal.</span><span style="color:#50FA7B">present</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Nothing more, nothing left, really easy. I like such solution.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*t5Ri1zehrQLJSVRHnt529w.png" alt="" / loading="lazy"></p>
<hr />
<h4>Modal To Page</h4>
<p>You might need to pass results from the modal to the page or calling components. To do so, we use the function <code>dismiss</code> , as when did to close the modal, but we pass an object as parameter.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">async </span><span style="color:#50FA7B">closeModalWithParams</span><span style="color:#F8F8F2">(greetings: string) {</span></span>
<span class="line"><span style="color:#FF79C6">  await</span><span style="color:#F8F8F2"> (</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.el.</span><span style="color:#50FA7B">closest</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">ion-modal</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#FF79C6">        as</span><span style="color:#8BE9FD;font-style:italic"> HTMLIonModalElement</span><span style="color:#F8F8F2">).</span><span style="color:#50FA7B">dismiss</span><span style="color:#F8F8F2">(greetings);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-button</span><span style="color:#50FA7B;font-style:italic"> onClick</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">closeModalWithParams</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Hello</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">     Say Hello!</span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ion-button</span><span style="color:#F8F8F2">></span></span></code></pre><p>In our example, I linked this new action with a new button.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*qMxPYUnuhe4jtFicx5_yjQ.png" alt="" / loading="lazy"></p>
<p>Finally, to handle the result, we listen to the <code>onDidDismiss</code> event of the modal and proceed with the details passed as callback.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Component, h, State} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@stencil/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {modalController, OverlayEventDetail} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@ionic/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  tag</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-home</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  styleUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-home.css</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> AppHome</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  @</span><span style="color:#50FA7B">State</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#F8F8F2"> greetingsResult</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#FF79C6"> async</span><span style="color:#50FA7B"> openModal</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> modal</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLIonModalElement</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#FF79C6">                 await</span><span style="color:#F8F8F2"> modalController.</span><span style="color:#50FA7B">create</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">      component</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-modal</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">      backdropDismiss</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">      componentProps</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        greetings</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">Yolo</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">      }</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    modal.</span><span style="color:#50FA7B">onDidDismiss</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">then</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#FF79C6">          async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">detail</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> OverlayEventDetail</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">      this</span><span style="color:#F8F8F2">.greetingsResult </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> detail.data;</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#F8F8F2"> modal.</span><span style="color:#50FA7B">present</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-content</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-button</span><span style="color:#50FA7B;font-style:italic"> onClick</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">openModal</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                    color</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">primary</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">          &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-label</span><span style="color:#F8F8F2">>Open Modal&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ion-label</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ion-button</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-label</span><span style="color:#F8F8F2">></span><span style="color:#FF79C6">{</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.greetingsResult</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ion-label</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ion-content</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>I used a state as demonstration purpose in order to render the results.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*fCDBeVvWKiRktdlAb7eTwQ.png" alt="" / loading="lazy"></p>
<p>Note that you can use both primitives types, complex objects, callbacks or events as parameters.</p>
<hr />
<h3>Cherry On The Cake</h3>
<p>It works exactly the same with <code>popovers.</code></p>
<hr />
<h3>See It In Action</h3>
<p>If you are interested to see Ionic modals used in Stencil apps in action, give a try to <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a> for your next slides 😁.</p>
<p>Stay home, stay safe!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Internationalization with Gatsby]]></title>
          <description><![CDATA[How to internationalize your Gatsby website with gatsby-plugin-i18n and react-intl in 2020]]></description>
          <link>https://daviddalbusco.com/blog/internationalization-with-gatsby/</link>
          <pubDate>Fri, 20 Mar 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*tuOyAQ0r1d4nnAcA1xccyQ.png" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@nicnut?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Nicola Nuttall</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I <a href="https://daviddalbusco.com/blog/how-to-call-the-service-worker-from-the-web-app-context" rel="noopener noreferrer external">challenged</a> my self to share a blog post every day until the end of the COVID-19 quarantine in Switzerland, the 19th April 2020. <strong>Thirty</strong> days left until hopefully better days.</p>
<hr />
<p>We are starting a new project with two friends, can’t tell much yet about it at this point, but let’s just say for the moment that it aligns our values. For its purpose we need a website which, obviously, is going to be open source and which I’m going to develop with <a href="https://www.gatsbyjs.org" rel="noopener noreferrer external">Gatsby</a>.</p>
<p>Even though it is not my first Gatsby’s site, my <a href="https://daviddalbusco.com" rel="noopener noreferrer external">personal website</a> is developed with the same stack, this is the first time I have to internationalize one.</p>
<p>I expected such implementation to be fairly straight forward but between light documentations, outdated blog posts or even sample projects, it turned out that I actually had to invest two hours this morning to finally achieve my goal.</p>
<p>That’s why I thought that sharing the outcome in this new tutorial might be a good idea.</p>
<hr />
<h3>SEO Friendly Plugin</h3>
<p>Your good old friend need different URLs (routes) to crawl and render your pages for each language. For example, if your website supports English and French, Google is going to be happy if you provide <code>https://domain.com/en/</code> and <code>https://domain.com/fr/</code> .</p>
<p>To achieve this with Gatsby, the first thing <strong>important</strong> to have in mind is that all your pages have to be duplicated. To follow above example, that would mean that the website would contains both an <code>index.en.js</code> page and an <code>index.fr.js</code> one.</p>
<p>To help our website understand such routing, we can use the plugin <a href="https://github.com/angeloocana/gatsby-plugin-i18n" rel="noopener noreferrer external">gatsby-plugin-i18n</a>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> install</span><span style="color:#F1FA8C"> gatsby-plugin-i18n</span><span style="color:#F1FA8C"> —-save</span></span></code></pre><p>Once installed, we add its required configuration in <code>gatsby-config.js</code> and also add some meta information about the list of supported languages and the default one.</p>
<p>Note that I specified <code>prefixDefault</code> to true in order to use no root routing, even URLs for the default language, English, will have to be prefixed with <code>/en/</code> . To be perfectly honest with you, one of the reason behind this is also the fact that I was unable to make it happens otherwise 😅.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">siteMetadata: {</span></span>
<span class="line"><span>  </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">languages</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span>    </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">langs</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">'en'</span><span style="color:#F8F8F2">, </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">'fr'</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span>    </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">defaultLangKey</span><span style="color:#FF79C6">:</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">'en'</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">},</span></span>
<span class="line"><span style="color:#F8F8F2">plugins: [</span></span>
<span class="line"><span style="color:#F8F8F2">  {</span></span>
<span class="line"><span>    </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">resolve</span><span style="color:#FF79C6">:</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">'gatsby-plugin-i</span><span style="color:#BD93F9">18</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">n'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span>    </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">options</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span>      </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">langKeyDefault</span><span style="color:#FF79C6">:</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">'en'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span>      </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">useLangKeyLayout</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span>      </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">prefixDefault</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">]</span></span></code></pre><p>Because we are using a prefix in any case, without any other change, accessing the root of our website will display nothing, that’s why we edit <code>gatsby-browser.js</code> to redirect the root requests to the default home page.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#8BE9FD;font-style:italic">exports</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">onClientEntry</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (window.location.pathname </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">/</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">        window.location.pathname </span><span style="color:#FF79C6">=</span><span style="color:#F1FA8C"> `/en`</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><hr />
<h3>Internationalization Library</h3>
<p>Gatsby and the above plugin are either compatible with <a href="https://react.i18next.com" rel="noopener noreferrer external">react-i18next</a> or <a href="https://github.com/formatjs/react-intl" rel="noopener noreferrer external">react-intl</a>. I use i18next in <a href="https://tietracker.app.link/" rel="noopener noreferrer external">Tie Tracker</a>, therefore I went with the other solution because I like to learn new things. React Intl relies on the <code>Intl</code> APIs, that’s why we are also installing the polyfill <a href="https://www.npmjs.com/package/@formatjs/intl-pluralrules" rel="noopener noreferrer external">intl-pluralrules</a>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> install</span><span style="color:#F1FA8C"> react-intl</span><span style="color:#F1FA8C"> @formatjs/intl-pluralrules</span><span style="color:#BD93F9"> --save</span></span></code></pre><hr />
<h3>Hands-on Coding</h3>
<p>Enough installation and configuration, let’s code. The major modification which we have to apply occurs in <code>layout.js</code> , which by the way, I moved in a subfolder <code>src/components/layout/</code> for no other particular reason that the fact that I like clean structure.</p>
<p>What happens here you may ask? Summarized, we are adding two new required properties, <code>location</code> and <code>messages</code> . The first one is use to guess the locale which should be applied and the second one contains the list of translations. As you can notice we import React Intl and we do also import a function <code>getCurrentLangKey</code> from <code>ptz-i18n</code> which is actually a utility of the above plugin.</p>
<p>I’m also using the <code>&lt;FormattedMessage/&gt;</code> component to print out an <code>Hello World</code> to ensure that our implementation works out.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> React </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> PropTypes </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">prop-types</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { useStaticQuery, graphql } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">gatsby</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> Header </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">../header</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./layout.css</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { FormattedMessage, IntlProvider } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">react-intl</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@formatjs/intl-pluralrules/polyfill</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { getCurrentLangKey } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">ptz-i18n</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> Layout</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> ({ </span><span style="color:#FFB86C;font-style:italic">children</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">location</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">messages</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> data </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useStaticQuery</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">graphql</span><span style="color:#F1FA8C">`</span></span>
<span class="line"><span style="color:#F1FA8C">        query SiteTitleQuery {</span></span>
<span class="line"><span style="color:#F1FA8C">            site {</span></span>
<span class="line"><span style="color:#F1FA8C">                siteMetadata {</span></span>
<span class="line"><span style="color:#F1FA8C">                    title</span></span>
<span class="line"><span style="color:#F1FA8C">                    languages {</span></span>
<span class="line"><span style="color:#F1FA8C">                        defaultLangKey</span></span>
<span class="line"><span style="color:#F1FA8C">                        langs</span></span>
<span class="line"><span style="color:#F1FA8C">                    }</span></span>
<span class="line"><span style="color:#F1FA8C">                }</span></span>
<span class="line"><span style="color:#F1FA8C">            }</span></span>
<span class="line"><span style="color:#F1FA8C">        }</span></span>
<span class="line"><span style="color:#F1FA8C">    `</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> { langs, defaultLangKey } </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> data.site.siteMetadata.languages;</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> langKey </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> getCurrentLangKey</span><span style="color:#F8F8F2">(langs, defaultLangKey, location.pathname);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IntlProvider</span><span style="color:#50FA7B;font-style:italic"> locale</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">langKey</span><span style="color:#FF79C6">}</span><span style="color:#50FA7B;font-style:italic"> messages</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">messages</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Header</span><span style="color:#50FA7B;font-style:italic"> siteTitle</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">data.site.siteMetadata.title</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#8BE9FD;font-style:italic">FormattedMessage</span><span style="color:#50FA7B;font-style:italic"> id</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">hello</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IntlProvider</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">Layout.propTypes </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    children</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> PropTypes.node.isRequired,</span></span>
<span class="line"><span style="color:#F8F8F2">    location</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> PropTypes.any.isRequired,</span></span>
<span class="line"><span style="color:#F8F8F2">    messages</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> PropTypes.any.isRequired</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> Layout;</span></span></code></pre><p>To “extend” the layout for each language and locale, we create a new file per supported languages. For example, in English, we create <code>layout/en.js</code> in which we import both our custom messages and the specific polyfill.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> React </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> Layout </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./layout</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> messages </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">../../i18n/en</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@formatjs/intl-pluralrules/dist/locale-data/en</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">props</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Layout</span><span style="color:#FF79C6"> {...</span><span style="color:#F8F8F2">props</span><span style="color:#FF79C6">}</span><span style="color:#50FA7B;font-style:italic"> messages</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">messages</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2"> />;</span></span></code></pre><p>At this point, our code won’t compile because these languages, these messages are missing. That’s why we also create the file for these, for example <code>i18n/en.js</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#8BE9FD;font-style:italic">module</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">exports</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    hello</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Hello world</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>As I briefly staten in my introduction, each page is going to be duplicated. That’s why we create the corresponding index page. In case of the default, English, we rename <code>index.js</code> to <code>index.en.js</code> . Moreover, because the layout now expect a location property, we pass it from every pages too.</p>
<p>Note also that, because I have decided to prefix all route, I also modifed the link routing from <code>/page-2/</code> to <code>/en/page-2</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> React </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Link } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">gatsby</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> Layout </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">../components/layout/en</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> SEO </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">../components/seo/seo</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> IndexPage</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">props</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Layout</span><span style="color:#50FA7B;font-style:italic"> location</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">props.location</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">SEO</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">>Hi people&#x3C;/</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Link</span><span style="color:#50FA7B;font-style:italic"> to</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/en/page-2/</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Go to page 2&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">Link</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">Layout</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> IndexPage;</span></span></code></pre><p>The same modifications we have implemented for <code>index</code> should be propagated to every pages, in this example, I also rename <code>page-2.js</code> in <code>page-2.en.js</code> and apply the same modifications as above.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> React </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Link } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">gatsby</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> Layout </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">../components/layout/en</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> SEO </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">../components/seo/seo</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> SecondPage</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">props</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Layout</span><span style="color:#50FA7B;font-style:italic"> location</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">props.location</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">SEO</span><span style="color:#50FA7B;font-style:italic"> title</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Page two</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">>Welcome to page 2&#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Link</span><span style="color:#50FA7B;font-style:italic"> to</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/en/</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Go back to the homepage&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">Link</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">Layout</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> SecondPage;</span></span></code></pre><p>Identically, the usage of the <code>&lt;Layout/&gt;</code> component has to be enhanced with the location object in our <code>404.js</code> page.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> React </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> Layout </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">../components/layout/layout</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> SEO </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">../components/seo/seo</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> NotFoundPage</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">props</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">Layout</span><span style="color:#50FA7B;font-style:italic"> location</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">props.location</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">SEO</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">>NOT FOUND&#x3C;/</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">Layout</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> NotFoundPage;</span></span></code></pre><p>And voilà, that’s it, our Gastby site is internationalized 🎉. Of course you might want to add some other languages, to do so, repeat the above English steps and again, duplicate pages.</p>
<hr />
<h3>More Tips And Tricks</h3>
<p>I have later on published a follow-up article, <a href="https://daviddalbusco.com/blog/more-gatsby-i18n-tips-and-tricks" rel="noopener noreferrer external">More Gatsby i18n Tips And Tricks</a>, about the internalization of Gatsby website. Check it out if you are eager to read more tips and tricks 😉.</p>
<hr />
<h3>Summary</h3>
<p>Well it was really unexpected to me to have had to spend so much time to unleash internationalization in a new project, that’s why I hope that this small “how to” might help anyone in the future. And as always, if you notice anything which can be improved, don’t hesitate to ping me with a comment or a <a href="https://twitter.com/daviddalbusco" rel="noopener noreferrer external">tweet</a>.</p>
<p>Stay home, stay safe!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Sometimes You Just Need A Dumb Library]]></title>
          <description><![CDATA[How to create a library with Rollup and Typescript #OneTrickADay-31]]></description>
          <link>https://daviddalbusco.com/blog/sometimes-you-just-need-a-dumb-library/</link>
          <pubDate>Thu, 19 Mar 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*3WQ_e2_-d02nvob2rlEJCA.png" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@bendavisual?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Benjamin Davies</a> on <a href="https://unsplash.com/s/photos/free?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I <a href="https://daviddalbusco.com/blog/how-to-call-the-service-worker-from-the-web-app-context" rel="noopener noreferrer external">challenged</a> my self to share a blog post very day until end of the current quarantine in Switzerland, the 19th April 2020. <strong>Thirty-one</strong> days left until hopefully better days.</p>
<hr />
<p>One of my client week’s assignment was to track down a bug between libraries. After quite a bit of time, I finally figured out that some code had been duplicated and had also received, of course, an improvement which had not been spread 😒.</p>
<p>Such issues can be avoided by not duplicating code, for example, by sharing it across projects through libraries. But often, if it is not yet something standardized in your company, it will take a bit of time to setup such new libraries.</p>
<p>Because in <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>, our open source editor for presentations, we actually had already setup such utilities, I thought it would be not a bad thing to share our recipe.</p>
<hr />
<h3>Our Goal: A Utility To Get Valid Date Objects</h3>
<p>Regardless of the JavaScript projects and the frameworks, at some point, I will most probably have to handle dates and most probably, I will have to cast these to proper JavaScript <code>Date</code> objects, or at least, have to ensure their validities. That’s why I created for my self a little function which does such job. Therefore I suggest that our goal in this article is to create a library which contains such a function.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> toDateObject</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">myDate</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> any</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Date</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">myDate </span><span style="color:#FF79C6">||</span><span style="color:#F8F8F2"> myDate </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (myDate </span><span style="color:#FF79C6">instanceof</span><span style="color:#8BE9FD;font-style:italic"> String</span><span style="color:#FF79C6"> ||</span><span style="color:#FF79C6"> typeof</span><span style="color:#F8F8F2"> myDate </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">string</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Date</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">myDate</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">typeof</span><span style="color:#F8F8F2"> myDate </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">number</span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> &#x26;&#x26;</span><span style="color:#FF79C6"> !</span><span style="color:#50FA7B">isNaN</span><span style="color:#F8F8F2">(myDate)) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Date</span><span style="color:#F8F8F2">(myDate);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // It case Firebase Timestamp format too</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (myDate </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> myDate.seconds </span><span style="color:#FF79C6">>=</span><span style="color:#BD93F9"> 0</span><span style="color:#FF79C6"> &#x26;&#x26;</span><span style="color:#F8F8F2"> myDate.nanoseconds </span><span style="color:#FF79C6">>=</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Date</span><span style="color:#F8F8F2">(myDate.</span><span style="color:#50FA7B">toDate</span><span style="color:#F8F8F2">());</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> myDate;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>Create A Library</h3>
<p>Let’s create our library. To begin with, in a terminal, we make a new folder and jump in it.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">mkdir</span><span style="color:#F1FA8C"> utils</span><span style="color:#F8F8F2"> &#x26;&#x26; </span><span style="color:#8BE9FD">cd</span><span style="color:#F1FA8C"> utils</span></span></code></pre><p>To develop and bundle our project we are going to use both <a href="https://rollupjs.org" rel="noopener noreferrer external">Rollup</a> and <a href="https://www.typescriptlang.org" rel="noopener noreferrer external">Typescript</a>. To install and use these, we create a new file <code>package.json</code> which exposes the following. Basically, use these two above libraries to prepare and build our library, <a href="https://github.com/isaacs/rimraf" rel="noopener noreferrer external">rimraf</a> to remove the output folder before each build and the information about the library itself respectively which file is going to be its <code>main</code> entry, which one is the <code>module</code> and which one the <code>types</code> definition.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">name</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">utils</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">version</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">1.0.0</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">devDependencies</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">@types/node</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">^13.9.1</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">rimraf</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">^3.0.2</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">rollup</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">^2.1.0</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">rollup-plugin-commonjs</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">^10.1.0</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">rollup-plugin-typescript</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">^1.0.1</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">tslib</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">^1.11.1</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">typescript</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">^3.8.3</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    },</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">main</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">lib/index.cjs.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">module</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">lib/index.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">types</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">lib/index.d.ts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">scripts</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">prepare</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">npm run build</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">build</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">rimraf lib &#x26;&#x26; rollup -c &#x26;&#x26; tsc</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">    },</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">files</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">lib</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">README.md</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">]</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h4>Typescript</h4>
<p>Before installing anything, we are going now to configure Typescript for example by specifying an ES2017 target in another new file <code>tsconfig.json</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">compilerOptions</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">target</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">ES2017</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">module</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">esnext</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">declaration</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">outDir</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">lib</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">strict</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#8BE9FE">        "</span><span style="color:#8BE9FD">removeComments</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span></span>
<span class="line"><span style="color:#F8F8F2">    },</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">include</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">src/**/*</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#8BE9FE">    "</span><span style="color:#8BE9FD">exclude</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">node_modules</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">]</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><hr />
<h3>Rollup</h3>
<p>Finally, last piece of the configuration I promise, we create a new <code>rollup.config.js</code> which, I guess according its name you already understand, is the configuration for Rollup. We are going to instruct it to create a CJS module and are also explicitly adding the Typescript support.</p>
<p>Note that you find also the references I used to create this bundle as commentaries in the following piece of code.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">// https://github.com/rollup/rollup-starter-lib</span></span>
<span class="line"><span style="color:#6272A4">// https://buzut.net/configurer-rollup-bundles-esm-cjs/</span></span>
<span class="line"><span style="color:#6272A4">// https://dev.to/proticm/how-to-setup-rollup-config-45mk</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> typescript </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">rollup-plugin-typescript</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> commonjs </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">rollup-plugin-commonjs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> pkg </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./package.json</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    input</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./src/index.ts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    plugins</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#50FA7B">commonjs</span><span style="color:#F8F8F2">(), </span><span style="color:#50FA7B">typescript</span><span style="color:#F8F8F2">()],</span></span>
<span class="line"><span style="color:#F8F8F2">    output</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        format</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">cjs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        file</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> pkg.main</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><hr />
<h4>Installation Of The Dependencies</h4>
<p>Everything is in place, we can now install the dependencies. Before doing so, we create an empty new file <code>index.ts</code> in a new folder <code>src</code> , otherwise the installation will end up in error, as a build is chained with the process.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">mkdir</span><span style="color:#F1FA8C"> src</span><span style="color:#F8F8F2"> &#x26;&#x26; </span><span style="color:#50FA7B">touch</span><span style="color:#F1FA8C"> src/index.ts</span></span>
<span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> install</span></span></code></pre><hr />
<h4>Coding</h4>
<p>If everything went according plan, we actually already have bundled an empty library 😁 but our goal is to expose the above function <code>toDateObject</code> . Therefore, we copy the related code in <code>src/index.ts</code> and once done, run the command to build the library.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> run</span><span style="color:#F1FA8C"> build</span></span></code></pre><p>And voilà, that’s it, we have a &quot;dumb&quot; library which can be use in all our projects 🎉.</p>
<hr />
<h3>Summary</h3>
<p>I don’t pretend to be any Rollup expert, if you notice anything which can be improved, ping me. I would love to hear it, specially as we have developed such libraries in <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>.</p>
<p>Speaking of, as we are open source, if you want to have a look, maybe some functions might suits your needs too, checkout our <a href="https://github.com/deckgo/deckdeckgo/tree/master/utils" rel="noopener noreferrer external">GitHub repo</a>.</p>
<p>Stay home, stay safe!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Inject JavaScript Or CSS At Runtime And On Demand]]></title>
          <description><![CDATA[How to load a JavaScript library, component or a style only when you really need it #OneTrickADay-32]]></description>
          <link>https://daviddalbusco.com/blog/inject-javascript-or-css-at-runtime-and-on-demand/</link>
          <pubDate>Wed, 18 Mar 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*NFVyLIxNmR6l8QJjFmzIpg.png" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@adityaries?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Aditya Saxena</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I <a href="https://daviddalbusco.com/blog/how-to-call-the-service-worker-from-the-web-app-context" rel="noopener noreferrer external">challenged</a> my self to share a blog post each and every single day until end of the current quarantine in Switzerland, the 19th April 2020. Thirty-two days left until hopefully better days.</p>
<p>In this third blog post, I would like to share with you a trick we are using in our open source project <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a> but also one which has been shared by <a href="https://twitter.com/Corysmc" rel="noopener noreferrer external">Cory McArthur</a>, an incredible user experience engineer of <a href="https://sworkit.com" rel="noopener noreferrer external">Sworkit</a>.</p>
<p>Commonly you are including your dependencies in your app bundle, but some of these might be used only in certain circumstances. For instance, if you are using <a href="https://github.com/firebase/firebaseui-web" rel="noopener noreferrer external">Firebase UI</a> to handle your authentication flow or if like us, you create a Web Component which act as a wrapper around another library like <a href="https://prismjs.com" rel="noopener noreferrer external">Prismjs</a>, you might want to load these only when really needed.</p>
<p>Even though a lazy loading pattern might be use in your app, depending of your UX and routing, you might rarely face the case where such libraries are fetched even if actually not needed.</p>
<p>But no worries, here’s a trick to solve such requirement by injecting either a script or css in your page on demand and at runtime.</p>
<h3>Load Conditionally A Script</h3>
<p>Let’s try to develop a new Web Component with <a href="https://stenciljs.com" rel="noopener noreferrer external">Stencil</a> which fetch a script when mounted. To do so, we run the following commands in a terminal:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> init</span><span style="color:#F1FA8C"> stencil</span></span>
<span class="line"><span style="color:#8BE9FD">cd</span><span style="color:#F1FA8C"> my-component</span></span>
<span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> install</span></span></code></pre><p>Once the project created, we edit the component and add a first test in order to verify if our script has not been added to the DOM before, because our component can be use multiple times in a page and we want to load our script only once.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Component, h } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@stencil/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    tag</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">my-component</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    styleUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">my-component.css</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    shadow</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> MyComponent</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    async</span><span style="color:#50FA7B"> componentDidLoad</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> scripts </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> document.</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">[myscript-loaded]</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">scripts) {</span></span>
<span class="line"><span style="color:#6272A4">            // TODO: load script</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">>Hello, World!&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Finally we can add our effective implementation which summarized works like the following: we create a new deferred <code>&lt;script/&gt;</code> which references the library or component we would like to load. Before adding it to the <code>header</code> of our page, we attach two events to handle both <code>success</code> or <code>error</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Component, h } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@stencil/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    tag</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">my-component</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    styleUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">my-component.css</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    shadow</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> MyComponent</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    async</span><span style="color:#50FA7B"> componentDidLoad</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> scripts </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> document.</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">[myscript-loaded]</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">scripts) {</span></span>
<span class="line"><span style="color:#FF79C6">            const</span><span style="color:#F8F8F2"> script </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> document.</span><span style="color:#50FA7B">createElement</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">script</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">            script.</span><span style="color:#50FA7B">onload</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">                script.</span><span style="color:#50FA7B">setAttribute</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">myscript-loaded</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">true</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">            };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">            script.</span><span style="color:#50FA7B">onerror</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">$err</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">                console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">($err);</span></span>
<span class="line"><span style="color:#F8F8F2">            };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">            script.src </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">https://unpkg.com/myscript.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">            script.defer </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">            document.head.</span><span style="color:#50FA7B">appendChild</span><span style="color:#F8F8F2">(script);</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">>Hello, World!&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>And…that’s it 🎉. By injecting the script in the header, the browser notices the change and proceeds it as it would normally do with any scripts.</p>
<h3>Cory’s Generic Functions</h3>
<p>Above solution is cool but generic functions are way cooler and handier 😉. Therefore here is Cory’s awesome solution to load any JavaScript or CSS on demand:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">function</span><span style="color:#50FA7B"> injectJS</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">id</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">src</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">>((</span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">reject</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">document) {</span></span>
<span class="line"><span style="color:#50FA7B">      resolve</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">      return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (document.</span><span style="color:#50FA7B">getElementById</span><span style="color:#F8F8F2">(id)) {</span></span>
<span class="line"><span style="color:#50FA7B">      resolve</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">JS already loaded.</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">      return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> script </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> document.</span><span style="color:#50FA7B">createElement</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">script</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    script.id </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> id;</span></span>
<span class="line"><span style="color:#F8F8F2">    script.async </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    script.defer </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    script.src </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> src;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    script.</span><span style="color:#50FA7B">addEventListener</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">load</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, () </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> resolve</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">JS loaded.</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">));</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    script.</span><span style="color:#50FA7B">addEventListener</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">error</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, () </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> reject</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Error script.</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">));</span></span>
<span class="line"><span style="color:#F8F8F2">    script.</span><span style="color:#50FA7B">addEventListener</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">abort</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, () </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> reject</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Aborted.</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">));</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    document.head.</span><span style="color:#50FA7B">appendChild</span><span style="color:#F8F8F2">(script);</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">function</span><span style="color:#50FA7B"> injectCSS</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">id</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">src</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">>((</span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">reject</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">document) {</span></span>
<span class="line"><span style="color:#50FA7B">      resolve</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">      return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (document.</span><span style="color:#50FA7B">getElementById</span><span style="color:#F8F8F2">(id)) {</span></span>
<span class="line"><span style="color:#50FA7B">      resolve</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">CSS already loaded.</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">      return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> link </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> document.</span><span style="color:#50FA7B">createElement</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">link</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    link.id </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> id;</span></span>
<span class="line"><span style="color:#F8F8F2">    link.</span><span style="color:#50FA7B">setAttribute</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">rel</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">stylesheet</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    link.</span><span style="color:#50FA7B">setAttribute</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">href</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, src);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    link.</span><span style="color:#50FA7B">addEventListener</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">load</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, () </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> resolve</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">CSS loaded.</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">));</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    link.</span><span style="color:#50FA7B">addEventListener</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">error</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, () </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> reject</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Error css.</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">));</span></span>
<span class="line"><span style="color:#F8F8F2">    link.</span><span style="color:#50FA7B">addEventListener</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">abort</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, () </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> reject</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">CSS aborted.</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">));</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    document.head.</span><span style="color:#50FA7B">appendChild</span><span style="color:#F8F8F2">(link);</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Such utilities can notably use to load Firebase UI only when needed:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">await</span><span style="color:#50FA7B"> injectJS</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">firebase-ui-script</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">https://cdn.firebase.com/libs/firebaseui/4.0.0/firebaseui.js</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">await</span><span style="color:#50FA7B"> injectCSS</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">firebase-ui-css</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://cdn.firebase.com/libs/firebaseui/4.0.0/firebaseui.css</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span></code></pre><h3>Summary</h3>
<p>One downside of the above solution, I’m agree, is the fact that you are handling a version number in, kind of, the middle of your code but to me, that’s a small trade of to be able to fetch some libraries only when needed because of the particular requirements of the UX of our editor, <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckgo</a>.</p>
<p>I warmly thank <a href="https://twitter.com/Corysmc" rel="noopener noreferrer external">Cory</a> for having shared his solution and also for having answered some of my questions, when I developed our authentication’s flow. Not all heroes wear capes, you are super Cory 🙏</p>
<p>Stay home, stay safe!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Replace Environment Variables In Your Index.html]]></title>
          <description><![CDATA[How to update environment variables and even add a SHA-256 in your application index.html without any plugins #OneTrickADay-33]]></description>
          <link>https://daviddalbusco.com/blog/replace-environment-variables-in-your-index/</link>
          <pubDate>Tue, 17 Mar 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*ez9DfR9-odbABx1E3E6Zcg.png" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@joshuaearle?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Joshua Earle</a> on <a href="https://unsplash.com/s/photos/free?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>Yesterday evening I began that crazy <a href="https://daviddalbusco.com/blog/how-to-call-the-service-worker-from-the-web-app-context" rel="noopener noreferrer external">challenge</a> to share a blog post each and every day until the quarantine is over here in Switzerland the 19th April 2020, 33 days left until hopefully better days.</p>
<p>In this second series’ article I would like to share with you another trick we have developed in our project <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>.</p>
<p>Even if we are open source and even share the credentials of our test environment directly in our <a href="https://github.com/deckgo/deckdeckgo" rel="noopener noreferrer external">GitHub</a> repo, we are keeping some, really few, production tokens hidden. Mostly because these are linked with our private credit cards 😅. That’s why, we have to replace environment variables at build time.</p>
<p>We have developed our frontend eco-system with the amazing compiler and toolchain <a href="https://stenciljs.com" rel="noopener noreferrer external">StencilJS</a> and I’ve already shared our solution to use variables in our code in two distinct posts (see <a href="https://daviddalbusco.com/blog/environment-variables-with-stenciljs" rel="noopener noreferrer external">here</a> and <a href="https://daviddalbusco.com/blog/hide-environment-variables-in-your-stenciljs-project" rel="noopener noreferrer external">there</a>). But, what I did not share so far is, how we replace environment variables in our <code>index.html</code> without any plugins 😃.</p>
<h3>Lifecycle NPM Scripts</h3>
<p>We want to replace variables after the build as completed. To hook on a corresponding lifecycle we are using <a href="https://docs.npmjs.com/misc/scripts" rel="noopener noreferrer external">npm-scripts</a> most precisely we are using <code>postbuild</code> . In our project, we create a vanilla Javascript file, for example <code>config.index.js</code> , and we reference it in the <code>package.json</code> file.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#E9F284">"</span><span style="color:#F1FA8C">scripts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">: {</span></span>
<span class="line"><span style="color:#8BE9FE">  "</span><span style="color:#8BE9FD">postbuild</span><span style="color:#8BE9FE">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./config.index.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><h3>Add Variable In Index.html</h3>
<p>Before implementing the script to update the variable per se, let’s first add a variable in our <code>index.html</code> . For example, let’s add a variable <code>&lt;@API_URL@&gt;</code> for the url of the API in our CSP rule.</p>
<p>Of course, out of the box, this content security policy will not be compliant as <code>&lt;@API_URL@&gt;</code> isn’t a valid url. Fortunately, in such case, the browser simply ignore the rule, which can be seen as convenient, because we can therefore work locally without any problems and without having to replace the value 😄.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">meta</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    http-equiv</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Content-Security-Policy</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    content</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">default-src 'self';</span></span>
<span class="line"><span style="color:#F1FA8C">  connect-src 'self' </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">&#x3C;</span><span style="color:#F1FA8C">@API_URLS@></span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">/></span></span></code></pre><h3>Update Script</h3>
<p>Configuration is in place, variable has been added, we just have now to implement the script. Basically, what it does, it finds all <code>html</code> pages (we use pre-rendering, therefore our bundle contains more than a single <code>index.html</code> ) and for each of these, read the content, replace the variable we have defined with a regex (not the clever one, I’m agree) and write back the results.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">#!/usr/bin/env node</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> fs </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> require</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">fs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> path </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> require</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">path</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">function</span><span style="color:#50FA7B"> updateCSP</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">filename</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">    fs.</span><span style="color:#50FA7B">readFile</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">filename</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">utf8</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#FF79C6">function</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">err</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">data</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#F8F8F2"> console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(err);</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> result </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> data.</span><span style="color:#50FA7B">replace</span><span style="color:#F8F8F2">(</span><span style="color:#FF5555">/</span><span style="color:#F1FA8C">&#x3C;@API_URLS@></span><span style="color:#FF5555">/</span><span style="color:#FF79C6">g</span><span style="color:#F8F8F2">, </span><span style="color:#F1FA8C">`https://myapi.com`</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        fs.</span><span style="color:#50FA7B">writeFile</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">filename</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">, result, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">utf8</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#FF79C6">function</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">err</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">            if</span><span style="color:#F8F8F2"> (err) </span><span style="color:#FF79C6">return</span><span style="color:#F8F8F2"> console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(err);</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">function</span><span style="color:#50FA7B"> findHTMLFiles</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">dir</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">files</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">    fs.</span><span style="color:#50FA7B">readdirSync</span><span style="color:#F8F8F2">(dir).</span><span style="color:#50FA7B">forEach</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">file</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> fullPath </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> path.</span><span style="color:#50FA7B">join</span><span style="color:#F8F8F2">(dir, file);</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (fs.</span><span style="color:#50FA7B">lstatSync</span><span style="color:#F8F8F2">(fullPath).</span><span style="color:#50FA7B">isDirectory</span><span style="color:#F8F8F2">()) {</span></span>
<span class="line"><span style="color:#50FA7B">            findHTMLFiles</span><span style="color:#F8F8F2">(fullPath, files);</span></span>
<span class="line"><span style="color:#F8F8F2">        } </span><span style="color:#FF79C6">else</span><span style="color:#FF79C6"> if</span><span style="color:#F8F8F2"> (path.</span><span style="color:#50FA7B">extname</span><span style="color:#F8F8F2">(fullPath) </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">.html</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">            files.</span><span style="color:#50FA7B">push</span><span style="color:#F8F8F2">(fullPath);</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">let</span><span style="color:#F8F8F2"> htmlFiles </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> [];</span></span>
<span class="line"><span style="color:#50FA7B">findHTMLFiles</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">./www/</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, htmlFiles);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">for</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> file </span><span style="color:#FF79C6">of</span><span style="color:#F8F8F2"> htmlFiles) {</span></span>
<span class="line"><span style="color:#50FA7B">    updateCSP</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`./</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">file</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Voilà, we are updating automatically at build time our environment variables in our application <code>index.html</code> 🎉</p>
<h3>Generate SHA-256 For Your CSP</h3>
<p>The above solution is cool but we actually had to go deeper. Each time we build our app, a <code>script</code> is going to be injected in our <code>index.html</code> in order to load the service worker. As we want to apply strict CSP rules, this script is going to be invalidated until we provide a SHA-256 exception for its representation. Of course, we weren’t looking forward to calculate it on each build and we have automated that task too. To do so, let’s first add a new variable in your <code>index.html</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">meta</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">  http-equiv</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Content-Security-Policy</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">  content</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">default-src 'self';</span></span>
<span class="line"><span style="color:#F1FA8C">  connect-src 'self' </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">&#x3C;</span><span style="color:#F1FA8C">@API_URLS@></span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">  script-src</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">'self'</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">&#x3C;@SW_LOADER@</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">/></span></span></code></pre><p>Once done, we now enhance the update script with a new function which takes care of finding the loading script (once again, not the cutest detection pattern, I’m agree), once found, generates its SHA-256 value and inject it as a new variable.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">#!/usr/bin/env node</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> fs </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> require</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">fs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> path </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> require</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">path</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> crypto </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> require</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">crypto</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">function</span><span style="color:#50FA7B"> updateCSP</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">filename</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">    fs.</span><span style="color:#50FA7B">readFile</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">filename</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">utf8</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#FF79C6">function</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">err</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">data</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#F8F8F2"> console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(err);</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        let</span><span style="color:#F8F8F2"> result </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> data.</span><span style="color:#50FA7B">replace</span><span style="color:#F8F8F2">(</span><span style="color:#FF5555">/</span><span style="color:#F1FA8C">&#x3C;@API_URLS@></span><span style="color:#FF5555">/</span><span style="color:#FF79C6">g</span><span style="color:#F8F8F2">, </span><span style="color:#F1FA8C">`https://myapi.com`</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> swHash </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> findSWHash</span><span style="color:#F8F8F2">(data);</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (swHash) {</span></span>
<span class="line"><span style="color:#F8F8F2">            result </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> result.</span><span style="color:#50FA7B">replace</span><span style="color:#F8F8F2">(</span><span style="color:#FF5555">/</span><span style="color:#F1FA8C">&#x3C;@SW_LOADER@></span><span style="color:#FF5555">/</span><span style="color:#FF79C6">g</span><span style="color:#F8F8F2">, swHash);</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        fs.</span><span style="color:#50FA7B">writeFile</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">filename</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">, result, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">utf8</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#FF79C6">function</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">err</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">            if</span><span style="color:#F8F8F2"> (err) </span><span style="color:#FF79C6">return</span><span style="color:#F8F8F2"> console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(err);</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">function</span><span style="color:#50FA7B"> findSWHash</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">data</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> sw </span><span style="color:#FF79C6">=</span><span style="color:#FF5555"> /</span><span style="color:#FFB86C">(</span><span style="color:#F1FA8C">&#x3C;</span><span style="color:#BD93F9">.</span><span style="color:#FF79C6">?</span><span style="color:#F1FA8C">script data-build</span><span style="color:#BD93F9">.</span><span style="color:#FF79C6">*?</span><span style="color:#F1FA8C">></span><span style="color:#FFB86C">)(</span><span style="color:#8BE9FD">[</span><span style="color:#BD93F9">\s\S</span><span style="color:#8BE9FD">]</span><span style="color:#FF79C6">*?</span><span style="color:#FFB86C">)(</span><span style="color:#F1FA8C">&#x3C;\/script></span><span style="color:#FFB86C">)</span><span style="color:#FF5555">/</span><span style="color:#FF79C6">gm</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> m;</span></span>
<span class="line"><span style="color:#FF79C6">    while</span><span style="color:#F8F8F2"> ((m </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> sw.</span><span style="color:#50FA7B">exec</span><span style="color:#F8F8F2">(data))) {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (m </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> m.length </span><span style="color:#FF79C6">>=</span><span style="color:#BD93F9"> 3</span><span style="color:#FF79C6"> &#x26;&#x26;</span><span style="color:#F8F8F2"> m[</span><span style="color:#BD93F9">2</span><span style="color:#F8F8F2">].</span><span style="color:#50FA7B">indexOf</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">serviceWorker</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">></span><span style="color:#FF79C6"> -</span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#F1FA8C"> `'sha256-</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">crypto.</span><span style="color:#50FA7B">createHash</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">sha256</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">).</span><span style="color:#50FA7B">update</span><span style="color:#F8F8F2">(m[</span><span style="color:#BD93F9">2</span><span style="color:#F8F8F2">]).</span><span style="color:#50FA7B">digest</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">base64</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">'`</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">function</span><span style="color:#50FA7B"> findHTMLFiles</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">dir</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">files</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">    fs.</span><span style="color:#50FA7B">readdirSync</span><span style="color:#F8F8F2">(dir).</span><span style="color:#50FA7B">forEach</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">file</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> fullPath </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> path.</span><span style="color:#50FA7B">join</span><span style="color:#F8F8F2">(dir, file);</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (fs.</span><span style="color:#50FA7B">lstatSync</span><span style="color:#F8F8F2">(fullPath).</span><span style="color:#50FA7B">isDirectory</span><span style="color:#F8F8F2">()) {</span></span>
<span class="line"><span style="color:#50FA7B">            findHTMLFiles</span><span style="color:#F8F8F2">(fullPath, files);</span></span>
<span class="line"><span style="color:#F8F8F2">        } </span><span style="color:#FF79C6">else</span><span style="color:#FF79C6"> if</span><span style="color:#F8F8F2"> (path.</span><span style="color:#50FA7B">extname</span><span style="color:#F8F8F2">(fullPath) </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">.html</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">            files.</span><span style="color:#50FA7B">push</span><span style="color:#F8F8F2">(fullPath);</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">let</span><span style="color:#F8F8F2"> htmlFiles </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> [];</span></span>
<span class="line"><span style="color:#50FA7B">findHTMLFiles</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">./www/</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, htmlFiles);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">for</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> file </span><span style="color:#FF79C6">of</span><span style="color:#F8F8F2"> htmlFiles) {</span></span>
<span class="line"><span style="color:#50FA7B">    updateCSP</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`./</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">file</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>That’s it, isn’t this handy?</p>
<h3>Summary</h3>
<p>As I said above, the regex and selector I used above aren’t the most beautiful one, but you know what, I’m not against improvements. If you are into it, don’t hesitate to send me a <a href="https://github.com/deckgo/deckdeckgo" rel="noopener noreferrer external">Pull Request</a> 😁.</p>
<p>Stay home, stay safe!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[How To Call The Service Worker From The Web App Context]]></title>
          <description><![CDATA[How To Call The Service Worker From The Web App Context #OneTrickADay-34]]></description>
          <link>https://daviddalbusco.com/blog/how-to-call-the-service-worker-from-the-web-app-context/</link>
          <pubDate>Mon, 16 Mar 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*4H2Zo7JLHzdfoDc-ltuy3A.png" alt="Photo by [Arindam Saha](https://unsplash.com/@flux_culture?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText) on [Unsplash](https://unsplash.com/s/photos/day-1?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText)" / loading="lazy"><em>Photo by <a href="https://unsplash.com/@flux_culture?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Arindam Saha</a> on <a href="https://unsplash.com/s/photos/day-1?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I literally just had such a bad idea I already know I might not make it, but well, it is worth a try 😅.</p>
<p>Switzerland goes on lockdown until 19th April 2020 and the state is asking us to remain at home. That’s why, I will try to share one trick a day until the end of the quarantine!</p>
<p>It is probably a bit optimistic, never set my self so far goals in terms of writing and I also already have preordered Disneyplus which suppose to begin is streaming really soon, but hey, you miss 100% of the shots you don’t take — Michael Scott.</p>
<h2>How To Call The Service Worker From The App Context</h2>
<p>I will not go too deep in details, but let’s just say that I recently merged a nice new <a href="https://github.com/deckgo/deckdeckgo/pull/654" rel="noopener noreferrer external">feature</a> in <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>, our open source editor for presentations 😉. For this purpose I used <a href="https://developers.google.com/web/tools/workbox" rel="noopener noreferrer external">Workbox</a> and I had to trigger on demand the caching from the web app context.</p>
<p>For example, let’s say you have defined a rule to cache your images.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">workbox.routing.</span><span style="color:#50FA7B">registerRoute</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#FF5555">    /</span><span style="color:#F1FA8C">\.</span><span style="color:#FFB86C">(?:</span><span style="color:#F1FA8C">png</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">gif</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">jpg</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">jpeg</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">webp</span><span style="color:#FF79C6">|</span><span style="color:#F1FA8C">svg</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">$</span><span style="color:#FF5555">/</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#FF79C6;font-weight:bold">    new</span><span style="color:#F8F8F2"> workbox.strategies.</span><span style="color:#50FA7B">CacheFirst</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        cacheName</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">images</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        plugins</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#FF79C6;font-weight:bold">            new</span><span style="color:#F8F8F2"> workbox.expiration.</span><span style="color:#50FA7B">Plugin</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">                maxAgeSeconds</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 30</span><span style="color:#FF79C6"> *</span><span style="color:#BD93F9"> 24</span><span style="color:#FF79C6"> *</span><span style="color:#BD93F9"> 60</span><span style="color:#FF79C6"> *</span><span style="color:#BD93F9"> 60</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">                maxEntries</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 60</span></span>
<span class="line"><span style="color:#F8F8F2">            })</span></span>
<span class="line"><span style="color:#F8F8F2">        ]</span></span>
<span class="line"><span style="color:#F8F8F2">    })</span></span>
<span class="line"><span style="color:#F8F8F2">);</span></span></code></pre><p>When you app starts, matching images being part of the current route are going to be cached automatically by Workbox, respectively by the service worker. But, maybe, you do use other images, which are not yet displayed, but which you might also want to cache for an offline use.</p>
<p>This can be solved by requesting the service worker to cache specific routes or urls from your web app content.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> addToCache</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> list </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/assets/img1.svg</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/assets/img2.svg</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> myCache </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> window.caches.</span><span style="color:#50FA7B">open</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">images</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#F8F8F2"> myCache.</span><span style="color:#50FA7B">addAll</span><span style="color:#F8F8F2">(list);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>And that’s it. With the help of the above function, taken from the <a href="https://developers.google.com/web/tools/workbox/guides/common-recipes" rel="noopener noreferrer external">documentation</a>, you can trigger the service worker to cache resources, routes or other requests on demand. Worth to notice that it can be called with or without user interaction, as you rather like.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">document.</span><span style="color:#50FA7B">addEventListener</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">DOMContentLoaded</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, (</span><span style="color:#FFB86C;font-style:italic">$event</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    addToCache</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">button</span><span style="color:#50FA7B;font-style:italic"> onclick</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">addToCache()</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Go images offline&#x3C;/</span><span style="color:#FF79C6">button</span><span style="color:#F8F8F2">>;</span></span></code></pre><p>That’s it, this was the first of the tricks I will try share during this quarantine but hopefully only one of really few articles.</p>
<p>Stay home, stay safe!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Introducing: Tie Tracker. A simple, open source and free time tracking app ⏱️]]></title>
          <description><![CDATA[Introducing: Tie Tracker. A simple, open source and free time tracking app ⏱️]]></description>
          <link>https://daviddalbusco.com/blog/introducing-tie-tracker-a-simple-open-source-and-free-time-tracking-app/</link>
          <pubDate>Wed, 11 Mar 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*jjeZp6_jbltdQSeOcTGdlg.png" alt="" / loading="lazy"></p>
<p>I’m happy to share with you <a href="https://tietracker.app.link" rel="noopener noreferrer external">Tie Tracker</a>: a simple, open source and free time tracking app ⏱️.</p>
<iframe width="280" height="158" src="https://www.youtube.com/embed/iXDPd6hShA0" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe
<br/>
<h3>Back Story</h3>
<p>Last December, in between clients' projects, I had some spare time to learn new concepts. Of all of interesting subjects out there, one particular retained my attention: improving my <a href="https://reactjs.org" rel="noopener noreferrer external">React</a> skills and giving a real try to <a href="https://react-redux.js.org" rel="noopener noreferrer external">Redux</a>.</p>
<p>Knowing my self, in comparison to experimenting, I knew I had to implement something concrete to get to feel comfortable with the technologies, specially with Redux. That’s why I came back to my long time idea to implement a time tracking and reporting application which perfectly matches my business processes.</p>
<p>Nevertheless, at this point, I was still unsure to start or not this project. After all, developing an application needs a certain effort. Moreover, I was not that motivated to develop yet again another solution which would need a cloud, authentication and database.</p>
<p>But after much thinking about it, I finally found the last bit of motivation: I would develop the app to run entirely offline, with the help of <a href="https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API" rel="noopener noreferrer external">IndexedDB</a>, and I would give a try to <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers" rel="noopener noreferrer external">Web Workers</a> to defer “heavy” computation.</p>
<p>And that was it, I was all in. I started my new <a href="https://ionicframework.com" rel="noopener noreferrer external">Ionic</a> app and I developed my new tool 😁.</p>
<h3>Features</h3>
<p>The application helps track productivity and billable hours for a list of clients and projects. Each entries can be billed. It also supports miscellaneous currencies and optionally a VAT rate.</p>
<ul>
<li>✅ Simple work hours tracking</li>
<li>✅ Assign time to clients and projects</li>
<li>✅ Mark entries as billed</li>
</ul>
<h3>Reporting</h3>
<p>For my company, I use a third party online accounting system to generate my client’s bill. When I send these, I join a report of every worked hours I spent on the projects for the selected period. So far, I was editing these timesheets manually but fortunately, I can now extract these on demand directly from Tie Tracker 😄.</p>
<ul>
<li>✅ Export open invoices to XLSX timesheets</li>
<li>✅ Weekly work summary</li>
<li>✅ Daily list of activities</li>
</ul>
<h3>Goodies</h3>
<p>Of course I had to implement some goodies 😉. One of these is an hourly reminder, through local notifications, about a task in progress. This notably took me some iterations before being stable, mobile development sometimes needs patience 😅.</p>
<p>I was also a little bit concerned about the persistence of the data on mobile devices, specially regarding iOS and its reliability with IndexedDB. To overcom this concern, I implemented a backup process: once a week, the application asks the user, if she/he would like to export all current not billed hours.</p>
<ul>
<li>Light and dark theme</li>
<li>Weekly backup</li>
<li>Hourly notification for task in progress (only mobile devices)</li>
</ul>
<h3>Open Source</h3>
<p>Obviously, at least for the few of you who are reading my articles time to time knowing that I have got an “open source mindset per default”, how could it have been different? Tie Tracker is open source, licensed under license AGPL v3 and above, its source code is available on <a href="https://github.com/peterpeterparker/tietracker" rel="noopener noreferrer external">GitHub</a> and contributions are most welcomed 🙏.</p>
<h3>What’s Next</h3>
<p>I have used <a href="https://tietracker.app.link/" rel="noopener noreferrer external">Tie Tracker</a> for three months now and I have billed several clients with it, therefore it already improved my daily work life. However, there are still two features I would like to develop and if there would be a public interests for an online mode in order to save the data in the cloud, I would consider to go further. Get in touch if you are interested!</p>
<p>To infinity and beyond 🚀</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Introducing A New Web Component To Drag, Resize And Rotate]]></title>
          <description><![CDATA[Add drag, drop and resize capabilities to any web apps]]></description>
          <link>https://daviddalbusco.com/blog/introducing-a-new-web-component-to-drag-resize-and-rotate/</link>
          <pubDate>Tue, 03 Mar 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*1pqUKDcoxfwUQ6jN6tZ0xQ.png" alt="" / loading="lazy"></p>
<p>Today we are happy to unleash a new open source Web Component that we developed for our web editor for presentations, <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>. With it you can add <strong>drag</strong>, <strong>resize</strong> and <strong>rotate</strong> features to your applications or websites, regardless of your web framework. Cherry on the cake: it works out of the box -- no extra JavaScript needed.</p>
<p>A demo is worth a thousand words:</p>
<p><img src="https://daviddalbusco.com/assets/images/1*_hmiwuTByWYJXuViGPBxFQ.gif" alt="" / loading="lazy"></p>
<h3>Back Story</h3>
<p>We really care about performance and dependencies. That’s why we tend to be a bit bundleophibic and, let’s face it, we are also nerds 🤷. Coding is as much a job as it is a hobby. That’s why we like to spend our evenings and weekends developing that kind of stuff.</p>
<p>A couple of months ago, when we were brainstorming new ideas, <a href="https://twitter.com/nasmattia" rel="noopener noreferrer external">Nicolas</a> suggested adding a new template which would let users draw technical schemas, directly in our editor. This concept implied two complexities: preserving the aspect ratio of the content of the slide across devices and being able to manipulate (drag, resize and rotate) its content. That’s why it took us some time to schedule and realize it.</p>
<p>While it may look easy to develop at first glance, during development we discovered that the required maths were actually more challenging than expected and therefore obviously even more fun 😉.</p>
<p><em>Nicolas published today a post in which he details the mathematical problems and solutions. <a href="https://nmattia.com/posts/2020-03-03-drag-resize-rotate.html" rel="noopener noreferrer external">Check it out on his blog</a>.</em></p>
<h3>Getting Started</h3>
<p>The easiest way to try out our component is to use it, with the help of <a href="https://unpkg.com/" rel="noopener noreferrer external">Unpkg</a>, in a plain HTML file.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">html</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">head</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">script</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            type</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">module</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://unpkg.com/@deckdeckgo/drag-resize-rotate@latest/dist/deckdeckgo-drag-resize-rotate/deckdeckgo-drag-resize-rotate.esm.js</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">        >&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">script</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            nomodule</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">""</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://unpkg.com/@deckdeckgo/drag-resize-rotate@latest/dist/deckdeckgo-drag-resize-rotate/deckdeckgo-drag-resize-rotate.js</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">        >&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">head</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">body</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">body</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">html</span><span style="color:#F8F8F2">></span></span></code></pre><p>Once the imported, the component can be used to drag, resize or rotate any elements. For that purpose, it should be just wrapped around each of these which have to be manipulated. For example, let’s say we have a division element.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> style</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">background: purple;</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span></code></pre><p>If we want to make it movable, draggable and resizable, we wrap it in our Web Component <code>&lt;deckgo-drr/&gt;</code>, we specify its default size and position with CSS4 variables and … that’s it 🎉.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">deckgo-drr</span><span style="color:#50FA7B;font-style:italic"> style</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">--width: 10%; --height: 19%; --top: 15%; --left: 12.5%;</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> style</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">background: purple;</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">deckgo-drr</span><span style="color:#F8F8F2">></span></span></code></pre><p>All together tested in the browser looks like the following.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*XUNHKbM_Q7ht6KyiHhDezA.gif" alt="" / loading="lazy"></p>
<h3>Options</h3>
<p>The cool thing about this component, I think, is that you don’t have to write any JavaScript to use it. You wrap it around any element and “it works”. It provides a couple of options, which are all documented in the related <a href="https://docs.deckdeckgo.com/components/drr" rel="noopener noreferrer external">chapter</a> of our documentation for developers. It notably supports various units (percent, viewport related or pixels) and each action can be disabled separately. Its design can be customized with various CSS4 variables and finally it bubbles two events, one when the component is selected or unselected and another one when it has changed.</p>
<p>It is also worth noticing that it does support both mouse and touch interactions.</p>
<h3>What’s Next</h3>
<p>What’s coming next is actually up to you 😉. We are open source and are eager to hear your feedback. Ping us on our <a href="https://join.slack.com/t/deckdeckgo/shared_invite/enQtNzM0NjMwOTc3NTI0LTBlNmFhODNhYmRkMWUxZmU4ZTQ2MDJiNjlmYWZiODNjMDU5OGRjYThlZmZjMTc5YmQ3MzUzMDlhMzk0ZDgzMDY" rel="noopener noreferrer external">Slack</a> channel, open an issue in our <a href="https://github.com/deckgo/deckdeckgo" rel="noopener noreferrer external">repo</a> or even provide a Pull Request, you are most welcome to contribute to our pet project <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a> in any ways or simply by using it to compose your next slides 🙏.</p>
<p>To infinity and beyond 🚀!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Syntax Highlighting Displayed In Ubuntu Terminal Like]]></title>
          <description><![CDATA[Display the code of your apps, websites or blogs with code highlighting in stylish Ubuntu Terminal like]]></description>
          <link>https://daviddalbusco.com/blog/syntax-highlighting-displayed-in-ubuntu-terminal-like/</link>
          <pubDate>Mon, 24 Feb 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*sJQnreNU9DZ0ARLHDil8gg.png" alt="" / loading="lazy"><br />
<em>Background photo by <a href="https://unsplash.com/@munmuns?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">MUNMUN SINGH</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>A couple of days ago, <a href="https://twitter.com/codyapearce" rel="noopener noreferrer external">Cody Pearce</a> published an <a href="https://dev.to/codypearce/ubuntu-terminal-in-css-1aeo" rel="noopener noreferrer external">article</a> which picked my curiosity. In his post he was displaying how the unique and immediately recognizable design of the Ubuntu terminal can be reproduced with CSS.</p>
<p>In <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>, our editor for presentations, we are providing a code highlighter Web Component, developed with <a href="https://stenciljs.com/" rel="noopener noreferrer external">Stencil</a>, which can be integrated in your slides, apps or even easily in a <a href="https://www.gatsbyjs.org/" rel="noopener noreferrer external">Gatsby</a> website.</p>
<p>That’s why I thought it would be fun to merge Cody’s idea and design 😄.</p>
<h3>HTML</h3>
<p>The quickest way to try out the component is probably to plug it, with the help of <a href="https://unpkg.com/" rel="noopener noreferrer external">Unpkg</a>, in a plain HTML file. Once the references to fetch its code have been added in the page header, it can be consumed.</p>
<p>Regarding the code to highlight, it should be provided to match the slot name <code>code</code> and that’s it, everything you need to highlight the code is ready.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">html</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">head</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">script</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            type</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">module</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://unpkg.com/@deckdeckgo/highlight-code@latest/dist/deckdeckgo-highlight-code/deckdeckgo-highlight-code.esm.js</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">        >&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">script</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            nomodule</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">""</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://unpkg.com/@deckdeckgo/highlight-code@latest/dist/deckdeckgo-highlight-code/deckdeckgo-highlight-code.js</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">        >&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">head</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">body</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">deckgo-highlight-code</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">code</span><span style="color:#50FA7B;font-style:italic"> slot</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">code</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>console.log('Hello World');&#x3C;/</span><span style="color:#FF79C6">code</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#FF79C6">deckgo-highlight-code</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">body</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">html</span><span style="color:#F8F8F2">></span></span></code></pre><p>Rendered in a browser, the above code looks like the following:</p>
<p><img src="https://daviddalbusco.com/assets/images/1*EQdSlwuhodPaVtTKzJ2JwQ.png" alt="" / loading="lazy"></p>
<p>As you may notice, it isn’t yet rendered in a stylish Ubuntu terminal like but in a “Macish” terminal, which is the default behavior. Therefore, to achieve our goal, we just improve the solution by providing the attribute <code>terminal</code> set to <code>ubuntu</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">deckgo-highlight-code</span><span style="color:#50FA7B;font-style:italic"> terminal</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ubuntu</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">code</span><span style="color:#50FA7B;font-style:italic"> slot</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">code</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>console.log('Hello World');&#x3C;/</span><span style="color:#FF79C6">code</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">deckgo-highlight-code</span><span style="color:#F8F8F2">></span></span></code></pre><p>And voilà, nothing more, nothing less 😁</p>
<p><img src="https://daviddalbusco.com/assets/images/1*K8ZJJL3WohfdOa8pKcCRlg.png" alt="" / loading="lazy"></p>
<p>By the way, don’t you feel too that it is over awesome to be able to write what, five lines of code in a plain HTML file and to already have a “complex” and performing element rendered? Damned, I love Web Components 🚀.</p>
<h3>Gatsby</h3>
<p>As briefly mentioned above, we also do provide a <a href="https://www.gatsbyjs.org/packages/gatsby-remark-highlight-code/" rel="noopener noreferrer external">plugin</a> to integrate easily our component in any Gatsby websites and blogs too. At the end of last year I published another <a href="https://daviddalbusco.com/blog/stylish-cards-and-syntax-highlighting-with-gatsby" rel="noopener noreferrer external">post</a> to display how it could be integrated, therefore I won’t cover the setup process again but just I just want to mention that the style could be selected through an optional plugin configuration:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">// In your gatsby-config.js</span></span>
<span class="line"><span style="color:#F8F8F2">plugins: [</span></span>
<span class="line"><span style="color:#F8F8F2">    {</span></span>
<span class="line"><span style="color:#F8F8F2">        resolve</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> `gatsby-transformer-remark`</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        options</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">            plugins</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#F8F8F2">                {</span></span>
<span class="line"><span style="color:#F8F8F2">                    resolve</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> `gatsby-remark-highlight-code`</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">                    options</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">                        terminal</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">ubuntu</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">                    }</span></span>
<span class="line"><span style="color:#F8F8F2">                }</span></span>
<span class="line"><span style="color:#F8F8F2">            ]</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">];</span></span></code></pre><p>If I would apply the above configuration to my personal website then, after rebuild, automatically all of its blocks of code would be encapsulated in Ubuntu terminal like.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*Bpmo6Lcv0WL5OiPRpTtIOA.png" alt="" / loading="lazy"></p>
<h3>Properties And Styling</h3>
<p>Beside the default configuration, the components supports <a href="https://docs.deckdeckgo.com/components/code" rel="noopener noreferrer external">many options</a>. Notably, because it is a wrapper around <a href="https://prismjs.com" rel="noopener noreferrer external">Prism.js</a>, the ability to highlight 205 languages (to be provided though a property <code>language</code> ) and offers many styling options through CSS4 variables. From specifying the color of the highlighted code to customizing the terminal or none, even if it’s a shadowed Web Component, it does expose many options and in case you would need more, ping me!</p>
<p>To infinity and beyond 🚀</p>
<p>David</p>
<p>P.S.: Even though the Ubuntu Terminal like are only going to be unleashed in our next major release, such stylish cards do already also look good in slides, so why not giving a try to <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a> for your next talk 😜</p>
<p><img src="https://daviddalbusco.com/assets/images/1*IpmwISIqDl__NE3SjrNdng.gif" alt="" / loading="lazy"></p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Reorder With Google Cloud Firestore]]></title>
          <description><![CDATA[How to maintain a list of data dynamically sorted with Cloud Firestore collection]]></description>
          <link>https://daviddalbusco.com/blog/reorder-with-google-cloud-firestore/</link>
          <pubDate>Wed, 19 Feb 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*wRwngS5dVwEFcnpF4t1q4w.jpeg" alt="" / loading="lazy"><br />
<em>Photo by <a href="https://unsplash.com/@hjrc33?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Héctor J. Rivas</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>Have you ever had the need to let your users order data as they wish to?</p>
<p>In one of my most recent work, one of the required feature had to do with giving the users the ability to reorder their data using Kanban boards. Likewise, in <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>, our web open source editor for presentations, users can sort slides according to their need.</p>
<p>In both cases, I use <a href="https://firebase.google.com/docs/firestore" rel="noopener noreferrer external">Cloud Firestore</a>, a scalable NoSQL cloud database from Google, and I implemented the same approach which I’ll try to described the best I can in this blog post.</p>
<h3>Approaches</h3>
<p>Firestore don’t offer out of box the ability to maintain data in collections ordered dynamically. It does give you the ability to perform sorted queries but doesn’t allow you to specify a custom ordering yet.</p>
<p>There are probably more than the following three solutions but, after thinking about the problem, I figured out that these were probably my best approaches to achieve my goal:</p>
<ol>
<li>Storing the data in document arrays instead of collections</li>
<li>Using linked list to keep track of the data's order</li>
<li>Saving the data in sub-collections and maintaining sorted arrays of their references in related documents</li>
</ol>
<p>For me, the first approach, storing data in arrays, was quickly a no go idea. It would have probably been the fastet solution to implement but I find it not scalable. The second one, linked lists, was interesting but I thought that the realization would be a bit verbose because I could imagine that each time an element of the list is modified, its adjacent nodes have to be updated too.</p>
<p>That’s why the only remaining approach was the third one, using arrays of references, which has the advantages of being scalable and not too verbose.</p>
<p>But, as great power comes with great responsibility, this solution has a small downside: it costs more than the two other solutions since it needs a bit more database operations.</p>
<h3>Model</h3>
<p>Let’s say that our goal, in this blog post, is being able to sort dynamically the slides of a presentation, respectively of a deck. To follow the above third approach, we are going to save the presentations in a parent-collection <code>decks</code> and the slides in a sub-collection <code>slides</code>. Moreover, as we want to take care of the ordering, we add an array <code>slides_ids</code> in the parent document which will contains the ordered list of ids.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">// Query: `/decks/${deckId}`</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> interface</span><span style="color:#8BE9FD;font-style:italic"> DeckData</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  slides_ids</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">[]; </span><span style="color:#6272A4">// &#x3C;-- The ordered slides ids</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> interface</span><span style="color:#8BE9FD;font-style:italic"> Deck</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  id</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  data</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> DeckData</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// Query: `/decks/${deckId}/slides/${slideId}`</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> interface</span><span style="color:#8BE9FD;font-style:italic"> SlideData</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  content</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">; </span><span style="color:#6272A4">// Just a dummy content for demo purpose</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> interface</span><span style="color:#8BE9FD;font-style:italic"> Slide</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  id</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  data</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> SlideData</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><h3>Implementation</h3>
<p>In order to split the implementation in separate parts we proceed with the following execution scenario. First we create a deck followed by the creation of three slides. We then implement a method to print the slides, because it is a good example of a retrieval function, and finally we implement a method to change the ordering of the slides.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> deckId </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> createDeck</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        await</span><span style="color:#50FA7B"> createSlide</span><span style="color:#F8F8F2">(deckId, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Slide 1</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">        await</span><span style="color:#50FA7B"> createSlide</span><span style="color:#F8F8F2">(deckId, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Slide 2</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">        await</span><span style="color:#50FA7B"> createSlide</span><span style="color:#F8F8F2">(deckId, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Slide 3</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        await</span><span style="color:#50FA7B"> printSlides</span><span style="color:#F8F8F2">(deckId);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        await</span><span style="color:#50FA7B"> moveSlide</span><span style="color:#F8F8F2">(deckId, </span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        await</span><span style="color:#50FA7B"> printSlides</span><span style="color:#F8F8F2">(deckId);</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#F8F8F2">        console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(err);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">})();</span></span></code></pre><h3>Create Data In The Parent-Collection</h3>
<p>The creation of the parent data, the <code>deck</code>, isn’t different as any data creation with Firestore. It doesn’t contain specific information regarding the ordering.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">async </span><span style="color:#50FA7B">createDeck</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> firestore </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> firebase.</span><span style="color:#50FA7B">firestore</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> data </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> {};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> doc </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> firestore.</span><span style="color:#50FA7B">collection</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">decks</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">).</span><span style="color:#50FA7B">add</span><span style="color:#F8F8F2">(data);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Deck created</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, {</span></span>
<span class="line"><span style="color:#F8F8F2">    id</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> doc.id,</span></span>
<span class="line"><span style="color:#F8F8F2">    data</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> data</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#F8F8F2"> doc.id;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p><em>In order to try to keep the demonstrated pieces of code clear and lean, please do note that in these I didn’t amended errors, performances and other subjects which are needed for a real implementation.</em></p>
<h3>Create Data In The Sub-Collection</h3>
<p>Likewise, creating the data in the sub-collection themselves, the <code>slides</code> , doesn’t contains any particular data regarding ordering <strong>but</strong> it does need an extra step to update the parent document because we want to keep track “manually” of the sorting.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">async </span><span style="color:#50FA7B">createSlide</span><span style="color:#F8F8F2">(deckId, content) {</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> firestore </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> firebase.</span><span style="color:#50FA7B">firestore</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> data </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    content</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> content</span></span>
<span class="line"><span style="color:#F8F8F2">  };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> doc </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> firestore.</span><span style="color:#50FA7B">collection</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`/decks/</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">deckId</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">/slides`</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">                    .</span><span style="color:#50FA7B">add</span><span style="color:#F8F8F2">(data);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Slide created</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, {</span></span>
<span class="line"><span style="color:#F8F8F2">    id</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> doc.id,</span></span>
<span class="line"><span style="color:#F8F8F2">    data</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> data</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  await</span><span style="color:#50FA7B"> updateDeck</span><span style="color:#F8F8F2">(deckId, doc.id);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>This extra step, the update of the <code>deck</code> , can for example be implemented like the following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">async </span><span style="color:#50FA7B">updateDeck</span><span style="color:#F8F8F2">(deckId, slideId) {</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> firestore </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> firebase.</span><span style="color:#50FA7B">firestore</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> snapshot </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> firestore</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">collection</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">decks</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">doc</span><span style="color:#F8F8F2">(deckId)</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">snapshot.exists) {</span></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Deck not found</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> data </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> snapshot.</span><span style="color:#50FA7B">data</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">data.slides_ids </span><span style="color:#FF79C6">||</span><span style="color:#F8F8F2"> data.slides_ids.length </span><span style="color:#FF79C6">&#x3C;=</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">    data.slides_ids.slides </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> [];</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">  // Add the newly created slide ID to the list of slides</span></span>
<span class="line"><span style="color:#F8F8F2">  data.slides_ids.</span><span style="color:#50FA7B">push</span><span style="color:#F8F8F2">(slideId);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  await</span><span style="color:#F8F8F2"> firestore</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">collection</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">decks</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">doc</span><span style="color:#F8F8F2">(deckId)</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">set</span><span style="color:#F8F8F2">(data, {merge</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Deck updated</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>But, in my opinion and because we are already using Firestore, the most reliable solution would be to defer the update of the slides’ list of IDs in a <a href="https://firebase.google.com/docs/functions" rel="noopener noreferrer external">Cloud Functions for Firebase</a>. For demonstration purpose I’ll stick to achieving the update from the client side but if you are implementing this solution in your application, I would suggest you to consider this option.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#BD93F9"> *</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> functions </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">firebase-functions</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> slideCreate </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> functions.firestore</span></span>
<span class="line"><span style="color:#F8F8F2">  .</span><span style="color:#50FA7B">document</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">decks/{deckId}/slides/{slideId}</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">  .</span><span style="color:#50FA7B">onCreate</span><span style="color:#F8F8F2">(watchSlideCreate);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> watchSlideCreate</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">snapshot</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">context</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> deckId</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> context.params.deckId;</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> slideId</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> context.params.slideId;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  await</span><span style="color:#50FA7B"> updateDeck</span><span style="color:#F8F8F2">(deckId, slideId);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// And adapt above `updateDeck` function to use 'firebase-admin'</span></span></code></pre><h3>Retrieve Ordered Data</h3>
<p>As mentioned in the approach, retrieving the “manually” ordered data costs more than querying these because we have first to get the list of IDs before being actually able to fetch these. But it does solve our goal.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">async </span><span style="color:#50FA7B">printSlides</span><span style="color:#F8F8F2">(deckId) {</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> firestore </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> firebase.</span><span style="color:#50FA7B">firestore</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> snapshot </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> firestore</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">collection</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">decks</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">doc</span><span style="color:#F8F8F2">(deckId)</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">snapshot.exists) {</span></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Deck not found</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> data </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> snapshot.</span><span style="color:#50FA7B">data</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">data.slides_ids </span><span style="color:#FF79C6">||</span><span style="color:#F8F8F2"> data.slides_ids.length </span><span style="color:#FF79C6">&#x3C;=</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">No slides to print</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> promises </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> data.slides_ids.</span><span style="color:#50FA7B">map</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">slideId</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#50FA7B"> printSlide</span><span style="color:#F8F8F2">(deckId, slideId);</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  await</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">all</span><span style="color:#F8F8F2">(promises);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>As you could notice, above we first fetch the deck and then map every single slides to a dedicated function to retrieve the related data.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">async </span><span style="color:#50FA7B">printSlide</span><span style="color:#F8F8F2">(deckId, slideId) {</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> firestore </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> firebase.</span><span style="color:#50FA7B">firestore</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> snapshot </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> firestore</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">collection</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`/decks/</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">deckId</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">/slides`</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">doc</span><span style="color:#F8F8F2">(slideId)</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">snapshot.exists) {</span></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Slide not found</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> data </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> snapshot.</span><span style="color:#50FA7B">data</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Slide print</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, data.content);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>It is also worth to notice, something I discovered recently, that it also offers some more flexibility in case you would be interested to develop a custom pagination. I won’t develop this topic in this particular article but if that would be interesting to you, ping me with a comment, I’ll be happy to develop this in a new blog post.</p>
<h3>Update Order</h3>
<p>If retrieving costs more, the beauty of this solution is maybe the fact that updating the order doesn’t costs much, because the list of sorted data is contained in a single document and therefore a single update query on the indexed array is already enough to define the new order.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">async </span><span style="color:#50FA7B">moveSlide</span><span style="color:#F8F8F2">(deckId, from, to) {</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> firestore </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> firebase.</span><span style="color:#50FA7B">firestore</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> snapshot </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> firestore</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">collection</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">decks</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">doc</span><span style="color:#F8F8F2">(deckId)</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">snapshot.exists) {</span></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Deck not found</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> data </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> snapshot.</span><span style="color:#50FA7B">data</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">data.slides_ids </span><span style="color:#FF79C6">||</span><span style="color:#F8F8F2"> data.slides_ids.length </span><span style="color:#FF79C6">&#x3C;=</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">No slides to move</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  data.slides_ids.</span><span style="color:#50FA7B">splice</span><span style="color:#F8F8F2">(to, </span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">, </span><span style="color:#FF79C6">...</span><span style="color:#F8F8F2">data.slides_ids.</span><span style="color:#50FA7B">splice</span><span style="color:#F8F8F2">(from, </span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">));</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  await</span><span style="color:#F8F8F2"> firestore</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">collection</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">decks</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">doc</span><span style="color:#F8F8F2">(deckId)</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">set</span><span style="color:#F8F8F2">(data, {merge</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Deck updated</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>In this particular example we don’t modify any other informations of the <code>slides</code> and that’s why I performed the update of the order from the client side but in the same way as I suggested in a previous chapter, if that would be the case, I would suggest to again defer such update in a cloud function.</p>
<h3>Epilogue</h3>
<p>I'm honestly not sure my above explanations where that clear. I really wanted to share the subject because it is a feature's requirements I do face often when I implement applications.</p>
<p>I hope this will someday help someone and if you have any comments or ideas, ping me about it or maybe even better, create a presentation with our editor <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a> and don’t forget to try to order manually your slides 😉</p>
<p>To infinity and beyond 🚀</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Create A React Custom Hooks For Your Web Components]]></title>
          <description><![CDATA[How to create a React custom hooks to bind your Web Components events with useEffect]]></description>
          <link>https://daviddalbusco.com/blog/create-a-react-custom-hooks-for-your-web-components/</link>
          <pubDate>Wed, 12 Feb 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*5KdZblpLM1bckDE3zr4kFA.jpeg" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@thenightstxlker?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Tamara Gore</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>When I walk up this morning, I said to myself: “Look David, now is the day, you should try to develop a <a href="https://reactjs.org" rel="noopener noreferrer external">React</a> custom hooks”.</p>
<p>The experiment went well and was implemented faster than I expected, therefore I thought I could take some time to write about it 😁.</p>
<h3>Introduction</h3>
<p>Web Components are working everywhere, period. That being said, when used in React, the implementation tends to become a bit more verbose, notably because <code>events</code> have to be attached “manually”. For example, you would not be able <strong>out of the box</strong> with a <a href="https://stenciljs.com" rel="noopener noreferrer external">Stencil</a> Web Component to do the following.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">my-component</span><span style="color:#50FA7B;font-style:italic"> onMyEvent</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">$event</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">($event)</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">my-component</span><span style="color:#F8F8F2">></span></span></code></pre><p>To overcome this issue, you could bundle your Web Component with their related output targets using the <a href="https://github.com/ionic-team/stencil-ds-plugins" rel="noopener noreferrer external">stencil-ds-plugins</a> and the problem is solved. But if you don’t, or can’t, then you have to manually register event listeners which, as I said above, could quickly become a bit verbose.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> ref </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useRef</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">ref.current.</span><span style="color:#50FA7B">addEventListener</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">myEvent</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, (</span><span style="color:#FFB86C;font-style:italic">$event</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">($event));</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">my-component</span><span style="color:#50FA7B;font-style:italic"> ref</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">ref</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">my-component</span><span style="color:#F8F8F2">>;</span></span></code></pre><p>Fortunately, it is possible to create custom hooks and therefore possible to create reusable pieces of code for our application to make it more readable.</p>
<h3>Let’s Get Started</h3>
<p>For the purpose of this article we are going to start with the very begin, by creating a new React app.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npx</span><span style="color:#F1FA8C"> create-react-app</span><span style="color:#F1FA8C"> custom-hook-app</span></span>
<span class="line"><span style="color:#8BE9FD">cd</span><span style="color:#F1FA8C"> custom-hook-app</span></span></code></pre><p>We want to experiment Web Component, let’s now install one to our application. For example, we can use the <a href="https://docs.deckdeckgo.com/components/color" rel="noopener noreferrer external">color picker</a> of our web open source editor for presentations, <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> install</span><span style="color:#F1FA8C"> @deckdeckgo/color</span></span></code></pre><p>Once installed, we can <code>import</code> and declare it in you application respectively in <code>src/App.js</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> React, { useEffect, useRef, useState } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> logo </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./logo.svg</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./App.css</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { defineCustomElements } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@deckdeckgo/color/dist/loader</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#50FA7B">defineCustomElements</span><span style="color:#F8F8F2">(window);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">function</span><span style="color:#50FA7B"> App</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> className</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">App</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">header</span><span style="color:#50FA7B;font-style:italic"> className</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">App-header</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#8BE9FD;font-style:italic">deckgo-color</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">deckgo-color</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF79C6">img</span><span style="color:#50FA7B;font-style:italic"> src</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">logo</span><span style="color:#FF79C6">}</span><span style="color:#50FA7B;font-style:italic"> className</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">App-logo</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> alt</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">logo</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                    Edit &#x3C;</span><span style="color:#FF79C6">code</span><span style="color:#F8F8F2">>src/App.js&#x3C;/</span><span style="color:#FF79C6">code</span><span style="color:#F8F8F2">> and save to reload.</span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF79C6">a</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                    className</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">App-link</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                    href</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://reactjs.org</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                    target</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">_blank</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                    rel</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">noopener noreferrer</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">                ></span></span>
<span class="line"><span style="color:#F8F8F2">                    Learn React</span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;/</span><span style="color:#FF79C6">a</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#FF79C6">header</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> App;</span></span></code></pre><p>If everything went according plan, once we run (<code>npm run start</code> ) our application, the default sample page with our color picker should be rendered.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*UkXaxAlAOLW955WUmaLm2g.gif" alt="" / loading="lazy"></p>
<h3>Implement The Events Listener</h3>
<p>Before creating our custom hooks, let’s first implement the events listener as we would do without it. We create a reference <code>useRef</code> for our component and a <code>state</code> to render the selected color.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> colorRef </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useRef</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> [color, setColor] </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useState</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> className</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">App</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">header</span><span style="color:#50FA7B;font-style:italic"> className</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">App-header</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#8BE9FD;font-style:italic">deckgo-color</span><span style="color:#50FA7B;font-style:italic"> ref</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">colorRef</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">deckgo-color</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">img</span><span style="color:#50FA7B;font-style:italic"> src</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">logo</span><span style="color:#FF79C6">}</span><span style="color:#50FA7B;font-style:italic"> className</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">App-logo</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> alt</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">logo</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> style</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">{ background</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> color }</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#FF79C6">header</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">);</span></span></code></pre><p>Finally, to attach the events, we use the hooks <code>useEffect</code> to bind these when our component’s reference is ready.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">useEffect</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> ref </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> colorRef.current;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#50FA7B"> colorListener</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">$event</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#6272A4">        // $event.detail.hex is the selected color</span></span>
<span class="line"><span style="color:#50FA7B">        setColor</span><span style="color:#F8F8F2">($event.detail.hex);</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // attach the event to the component</span></span>
<span class="line"><span style="color:#F8F8F2">    ref.</span><span style="color:#50FA7B">addEventListener</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">colorChange</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, colorListener, </span><span style="color:#BD93F9">false</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // remove event on component unmount</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        ref.</span><span style="color:#50FA7B">removeEventListener</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">colorChange</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, colorListener, </span><span style="color:#BD93F9">true</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"><span style="color:#F8F8F2">}, [colorRef]);</span></span></code></pre><p>I’m agree, not the best UX I ever developed 🤣, but still, we should now be able to select colors and apply them to the background of the React logo.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*sCVKl0QyIYWC0quTalcgzg.gif" alt="" / loading="lazy"></p>
<h3>Create A Custom Hooks</h3>
<p>Time to have fun by refactoring our previous implementation in order to create a custom hooks. Firstly, we create a function, the hooks itself, which takes the reference to the component as parameter, contains and return a new state.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">function</span><span style="color:#50FA7B"> useColorChange</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">paramColorRef</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> [data, setData] </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useState</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">undefined</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> [data];</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>To complete our hooks, we move our previous <code>useEffect</code> code to this new hooks and we adjust the component states to the hooks states. The effect watches the reference passed as parameters and the listener applies the selected color to the hooks state.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">function</span><span style="color:#50FA7B"> useColorChange</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">paramColorRef</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> [data, setData] </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useState</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">undefined</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    useEffect</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> ref </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> paramColorRef.current;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#50FA7B"> colorListener</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">$event</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">            setData</span><span style="color:#F8F8F2">($event.detail.hex);</span></span>
<span class="line"><span style="color:#F8F8F2">        };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        ref.</span><span style="color:#50FA7B">addEventListener</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">colorChange</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, colorListener, </span><span style="color:#BD93F9">false</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">            ref.</span><span style="color:#50FA7B">removeEventListener</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">colorChange</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, colorListener, </span><span style="color:#BD93F9">true</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">        };</span></span>
<span class="line"><span style="color:#F8F8F2">    }, [paramColorRef]);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> [data];</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Finally, we use our hooks in our application respectively we replace the previous <code>useState</code> and <code>useEffect.</code></p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">function</span><span style="color:#50FA7B"> App</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> colorRef </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useRef</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> [color] </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useColorChange</span><span style="color:#F8F8F2">(colorRef);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> className</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">App</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">header</span><span style="color:#50FA7B;font-style:italic"> className</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">App-header</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#8BE9FD;font-style:italic">deckgo-color</span><span style="color:#50FA7B;font-style:italic"> ref</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">colorRef</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">deckgo-color</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF79C6">img</span><span style="color:#50FA7B;font-style:italic"> src</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">logo</span><span style="color:#FF79C6">}</span><span style="color:#50FA7B;font-style:italic"> className</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">App-logo</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> alt</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">logo</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> style</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">{ background</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> color }</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#FF79C6">header</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Voilà, isn’t that a cleaner code and pretty cool? And of course, if we redo our test, it should still work out, we should still be able to select a color and apply it to the background of the React logo 😸.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*xZ8UwUQcpfn_15YDwmekew.gif" alt="" / loading="lazy"></p>
<h3>Conclusion</h3>
<p>I don’t pretend that the above implementation is the best one, my goal was to try to build a React custom hooks out and to share a comprehensive step by step blog post. I’m pretty sure it could be improved and I would be super duper happy to hear your suggestions about it, ping me with your comments!</p>
<p>To infinity and beyond 🚀</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Firebase Storage Gotchas 😅]]></title>
          <description><![CDATA[Sharing some Google Cloud Storage key points I learned while implementing private assets and reverting two days of work.]]></description>
          <link>https://daviddalbusco.com/blog/firebase-storage-gotchas/</link>
          <pubDate>Tue, 04 Feb 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*58twqItpOjtMRuQquE2l5w.jpeg" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@element5digital?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Element5 Digital</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>When was the last time you reverted several working days?</p>
<p>I recently took some time to make the assets, moreover than the content, private for every users of our web editor for presentations, <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>.</p>
<p>After two working days, I finally noticed that I misinterpreted one fundamental point of the <a href="https://firebase.google.com/docs/storage" rel="noopener noreferrer external">Google Cloud Storage</a> and I figured out that I had to revert my new implementation, because our assets were actually, already private 😅. That’s why I’m writing this new blog post, hoping that my “unlucky” experience might help someone else in the future.</p>
<h3>Rules: Storage Does Not Have Access To Firestore</h3>
<p>Sometimes when things are not written down, I'm asking my self if they aren't, because they are not possible or because they actually are possible 🤔.</p>
<p>Being able to write Storage rules by querying <a href="https://firebase.google.com/docs/firestore" rel="noopener noreferrer external">Firestore</a> was one of these things and the answer is <strong>no.</strong> There is currently no possibility to access a Firebase product from another product.</p>
<h3>Rules: Users Read And Write Privileges</h3>
<p>It is possible to restrict the access, read and write, to the storage to only authenticated users.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">rules_version </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">2</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">service firebase.storage {</span></span>
<span class="line"><span style="color:#F8F8F2">  match </span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2">b</span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2">{bucket}</span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2">o {</span></span>
<span class="line"><span style="color:#F8F8F2">    match </span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2">{allPaths</span><span style="color:#FF79C6">=**</span><span style="color:#F8F8F2">} {</span></span>
<span class="line"><span style="color:#F8F8F2">      allow read, write: </span><span style="color:#FF79C6">if</span><span style="color:#F8F8F2"> request.auth </span><span style="color:#FF79C6">!=</span><span style="color:#BD93F9"> null</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>But the above rule still implies that users would be able overwrite data provided by other users. To overcome this problem, we can prefix the data of each users in the Storage with their respective <code>userId</code>.</p>
<p>For example, if you are using the <a href="https://github.com/firebase/firebase-js-sdk" rel="noopener noreferrer external">Firebase JavaScript SDK</a>, an upload would look like the following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> ref</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Reference</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#F8F8F2">      firebase.</span><span style="color:#50FA7B">storage</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">ref</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">userId</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">/assets/photo.jpg`</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">await</span><span style="color:#F8F8F2"> ref.</span><span style="color:#50FA7B">put</span><span style="color:#F8F8F2">(data);</span></span></code></pre><p>Once the storage files ordered in that structure, we are then be able to define a rule like the following which only allows users to write and read data in their respective folder of the storage:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">rules_version </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">2</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">service firebase.storage {</span></span>
<span class="line"><span style="color:#F8F8F2">  match </span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2">b</span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2">{bucket}</span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2">o {</span></span>
<span class="line"><span style="color:#F8F8F2">    match </span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2">{userId}</span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2">assets</span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2">{allPaths</span><span style="color:#FF79C6">=**</span><span style="color:#F8F8F2">} {</span></span>
<span class="line"><span style="color:#F8F8F2">      allow read, write: </span><span style="color:#FF79C6">if</span><span style="color:#F8F8F2"> request.auth.uid </span><span style="color:#FF79C6">==</span><span style="color:#F8F8F2"> userId;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><h3>Format: Storage References</h3>
<p>To access a public file or a private one, with a granted rule access, the Storage URL could be made of the following parts:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">img</span><span style="color:#50FA7B;font-style:italic"> src</span><span style="color:#FF79C6">=</span><span style="color:#F1FA8C">{</span><span style="color:#50FA7B;font-style:italic">`https:</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">//firebasestorage.googleapis.com/v0/b/${projectId}.appspot.com/o/${encodeURIComponent(path)}?alt=media`}/</span><span style="color:#F8F8F2">></span></span></code></pre><p>Where <code>${projectId}</code> is the Id of the Firebase project and <code>${path}</code> the path to the file in the storage (a string) which has to be encoded with <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent" rel="noopener noreferrer external">encodeURIComponent</a> in order to be compliant.</p>
<h3>Fetch: Access Image Securely With OAuth2</h3>
<p>If we define the above rules or any other rules which grant access to the storage only to authenticated users, it is possible with JavaScript to fetch and load an image with OAuth2 as Bryan Burman displayed in his blog post “<a href="https://www.twelve21.io/how-to-access-images-securely-with-oauth-2-0/" rel="noopener noreferrer external">How To Access Images Securely with OAuth 2.0</a>”.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> imgSrc</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#E9F284">      '</span><span style="color:#F1FA8C">https://firebasestorage.googleapis.com/.../photo.jpg</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> rawResponse</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Response</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> fetch</span><span style="color:#F8F8F2">(imgSrc, {</span></span>
<span class="line"><span style="color:#F8F8F2">        method</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">GET</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        headers</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#E9F284">            '</span><span style="color:#F1FA8C">Authorization</span><span style="color:#E9F284">'</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> `Bearer </span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">firebase_user_auth_token</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">rawResponse </span><span style="color:#FF79C6">||</span><span style="color:#FF79C6"> !</span><span style="color:#F8F8F2">rawResponse.ok) {</span></span>
<span class="line"><span style="color:#F8F8F2">        console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`Image can not be fetched.`</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> blob</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Blob</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> rawResponse.</span><span style="color:#50FA7B">blob</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    document.</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">img</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">).src </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> URL</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">createObjectURL</span><span style="color:#F8F8F2">(blob);</span></span>
<span class="line"><span style="color:#F8F8F2">} </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(err);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><h3>downloadURL: Public But Private</h3>
<p>This was my biggest “gotcha” and the reason why I reverted hours of work.</p>
<p>For each and every single asset uploaded in the Storage, Firebase create, regardless if you use them or not, a <code>downloadUrl</code> which is <strong>public</strong>, regardless of your rules, and accessible on the internet. <strong>But</strong>, because the url contains a <code>token</code> which is essentially impossible for anyone to guess, these urls are, as long as we don’t share these with anyone, <strong>private</strong>.</p>
<p>Here’s for example <code>downloadUrl</code> where the token is use as parameter to grant the access to the file.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">img</span><span style="color:#50FA7B;font-style:italic"> src</span><span style="color:#FF79C6">=</span><span style="color:#F1FA8C">{</span><span style="color:#50FA7B;font-style:italic">`https:</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">//firebasestorage.googleapis.com/v0/b/${projectId}.appspot.com/o/${path}?alt=media&#x26;token=4733325a-78ff-444d-a67c-01fd8ab30fe`}/</span><span style="color:#F8F8F2">></span></span></code></pre><p>This was a turning point to me in the process and I have to thank <a href="https://twitter.com/CodingDoug" rel="noopener noreferrer external">Doug Stevenson</a> for having answered my question about it on <a href="https://stackoverflow.com/questions/59782590/firebase-storage-never-fully-private" rel="noopener noreferrer external">StackOverflow</a>.</p>
<p><em>Note that, without being absolutely sure, I think it might be possible if you are using the Cloud Solution from the server side to instruct Firebase to not generate such urls but it is definitely not possible from the Web/Client side.</em></p>
<h3>downloadUrl: Lifecycle</h3>
<p>Firebase Storage token does <strong>not</strong> expire (see <a href="https://stackoverflow.com/a/42598354" rel="noopener noreferrer external">StackOverflow</a>). Therefore, without any other modifications, our <code>downloadUrl</code> also never expire and remain available. <strong>But</strong>, is it possible in the Firebase Console, to invalidate a specific url. <strong>But</strong>, once invalidated, Firebase will create a new <code>downloadUrl</code> respectively a new <code>token</code> for the selected file.</p>
<p>It is also worth to notice, that for each overwrites, Firebase will also generate a new <code>token</code> . This means that for example if the users of ours applications are able to upload files, each time they would upload again a file without changing its name, a new <code>downloadUrl</code> would be automatically created.</p>
<p>That’s it, I think this is the summary of my learnings. Firebase Storage is definitely an incredible developer friendly piece of software and all the content and assets of every single users of <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a> is private until they decide to share publicly their presentations.</p>
<p>To infinity and beyond 🚀</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Infinite Scroll with Ionic, Angular and Firestore]]></title>
          <description><![CDATA[How to implement an infinite scroll with Ionic, Angular and Google Cloud Firestore]]></description>
          <link>https://daviddalbusco.com/blog/infinite-scroll-with-ionic-angular-and-firestore/</link>
          <pubDate>Mon, 27 Jan 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*yJGFgDFrXEnvCT3cEm_kOg.jpeg" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@dan_schiumarini?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Dan Schiumarini</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>Once again I find myself having to find a new place to live in Zürich City and therefore once again I have to use search engines and platforms to browse flats, that were never upgraded, in terms of UX and even sometimes design, since the ’90s 🙈.</p>
<p>Yesterday morning, while I was about to visit such websites, I realize that there are no ways to me to undergo the frustration of using these for weeks or months, again 😤. That’s why I spent my Sunday writing a personal crawler with <a href="https://github.com/puppeteer/puppeteer" rel="noopener noreferrer external">Puppeteer</a> and <a href="https://firebase.google.com/" rel="noopener noreferrer external">Google Firebase</a> and why I developed quickly today, an <a href="https://ionicframework.com" rel="noopener noreferrer external">Ionic</a> and <a href="https://angular.io" rel="noopener noreferrer external">Angular</a> app to browse the outcome. Because it is not the first time I program an <a href="https://ionicframework.com/docs/api/infinite-scroll" rel="noopener noreferrer external">Infinite Scroll</a> with such a technology stack, I finally came to the idea to share my implementation in this new blog post.</p>
<h3>Prerequisites</h3>
<p>In this post we are going to use Ionic, Angular, a <a href="https://cloud.google.com/firestore/" rel="noopener noreferrer external">Google Cloud Firestore</a> database and also <a href="https://github.com/angular/angularfire" rel="noopener noreferrer external">AngularFire</a> and <a href="https://rxjs-dev.firebaseapp.com/" rel="noopener noreferrer external">RxJS</a>. I am not going to describe how to install and configure each of these requirements. If you would face problems setting up these, don’t hesitate to contact me.</p>
<h3>Service</h3>
<p>One asset I like in Angular is the separation of concern. We create a new <code>service</code> which should take care of interacting with the database.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">ionic</span><span style="color:#F1FA8C"> g</span><span style="color:#F1FA8C"> service</span><span style="color:#F1FA8C"> feed</span></span></code></pre><p>In the following lines I’ll use the <code>interface</code> and generic name <code>Item</code> to represent the data we are looking to fetch and I’ll declare these interfaces in the same class as our service. Replace it with the real description of your data 😉.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {DocumentReference} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/fire/firestore</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">interface</span><span style="color:#8BE9FD;font-style:italic"> ItemData</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  title</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  content</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">interface</span><span style="color:#8BE9FD;font-style:italic"> Item</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  id</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  ref</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> DocumentReference</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  data</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> ItemData</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>In our newly created service, we declare the following variables:</p>
<ol>
<li><code>itemsSubject</code> : a state container for our items</li>
<li><code>lastPageReached</code> : another state, a <code>boolean</code> , to notice if we have or not yet fetched all the data</li>
<li><code>nextQueryAfter</code> : a reference to the last Firestore document fetched to index our database queries</li>
<li><code>paginationSub</code> and <code>findSub</code> : two subscriptions to stop observing the changes and to clean the memory when needed</li>
</ol>
<p>Moreover, we also declare a service to interact with Firestore, a method <code>destroy</code> to unsubscribe the observers and we expose two functions to return our subjects as observables.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Injectable} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {AngularFirestore, DocumentReference, QueryDocumentSnapshot}</span></span>
<span class="line"><span style="color:#FF79C6">  from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/fire/firestore</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {BehaviorSubject, Subscription} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">rxjs</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Injectable</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  providedIn</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">root</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> FeedService</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#F8F8F2"> itemsSubject</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> BehaviorSubject</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Item</span><span style="color:#F8F8F2">[] </span><span style="color:#FF79C6">|</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#FF79C6;font-weight:bold">                        new</span><span style="color:#50FA7B"> BehaviorSubject</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">undefined</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#F8F8F2"> lastPageReached</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> BehaviorSubject</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">boolean</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#FF79C6;font-weight:bold">                           new</span><span style="color:#50FA7B"> BehaviorSubject</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">false</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#F8F8F2"> nextQueryAfter</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> QueryDocumentSnapshot</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">ItemData</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#F8F8F2"> paginationSub</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Subscription</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#F8F8F2"> findSub</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Subscription</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  constructor</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">private</span><span style="color:#FFB86C;font-style:italic"> fireStore</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> AngularFirestore</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  destroy</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">unsubscribe</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#50FA7B"> unsubscribe</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.paginationSub) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">      this</span><span style="color:#F8F8F2">.paginationSubscription.</span><span style="color:#50FA7B">unsubscribe</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.findSub) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">      this</span><span style="color:#F8F8F2">.findSubscription.</span><span style="color:#50FA7B">unsubscribe</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  watchItems</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Observable</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Item</span><span style="color:#F8F8F2">[]> {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.itemsSubject.</span><span style="color:#50FA7B">asObservable</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  watchLastPageReached</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Observable</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">boolean</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.lastPageReached.</span><span style="color:#50FA7B">asObservable</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>We have to query the data in Firestore step by step respectively using a pagination because we are going to implement an infinite scroller. For that purpose, Google provides <a href="https://firebase.google.com/docs/firestore/query-data/query-cursors" rel="noopener noreferrer external">startAfter</a> which instruct the database to “skip” the matching entities before the given start point. It is also worth to notice that in order to be able to perform such queries, we also need to sort these with <code>orderBy</code> and that I limited the pagination to 10 elements per step with the option <code>limit</code>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">find</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">  try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> collection</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> AngularFirestoreCollection</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">ItemData</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">                      this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">getCollectionQuery</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">unsubscribe</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.paginationSub </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> collection.</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">                         .</span><span style="color:#50FA7B">subscribe</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">first</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">      this</span><span style="color:#F8F8F2">.nextQueryAfter </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> first.docs[first.docs.length </span><span style="color:#FF79C6">-</span><span style="color:#BD93F9"> 1</span><span style="color:#F8F8F2">] </span><span style="color:#FF79C6">as</span></span>
<span class="line"><span style="color:#F8F8F2">                            QueryDocumentSnapshot</span><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2">ItemData</span><span style="color:#FF79C6">></span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">      await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">query</span><span style="color:#F8F8F2">(collection);</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">  } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#FF79C6">    throw</span><span style="color:#F8F8F2"> err;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">private </span><span style="color:#50FA7B">getCollectionQuery</span><span style="color:#F8F8F2">(): AngularFirestoreCollection</span><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2">ItemData</span><span style="color:#FF79C6">></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">  if</span><span style="color:#F8F8F2"> (this.nextQueryAfter) {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.fireStore.</span><span style="color:#50FA7B">collection</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">ItemData</span><span style="color:#F8F8F2">>(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">/items/</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">ref</span><span style="color:#FF79C6"> =></span></span>
<span class="line"><span style="color:#F8F8F2">           ref.</span><span style="color:#50FA7B">orderBy</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">created_at</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">desc</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">             .</span><span style="color:#50FA7B">startAfter</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.nextQueryAfter)</span></span>
<span class="line"><span style="color:#F8F8F2">             .</span><span style="color:#50FA7B">limit</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">10</span><span style="color:#F8F8F2">));</span></span>
<span class="line"><span style="color:#F8F8F2">  } else {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.fireStore.</span><span style="color:#50FA7B">collection</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">ItemData</span><span style="color:#F8F8F2">>(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">/items/</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">ref</span><span style="color:#FF79C6"> =></span></span>
<span class="line"><span style="color:#F8F8F2">           ref.</span><span style="color:#50FA7B">orderBy</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">created_at</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">desc</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">             .</span><span style="color:#50FA7B">limit</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">10</span><span style="color:#F8F8F2">));</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>To that point, we have implemented a <code>find</code> function which query the database, therefore we can now develop the part where we collect the results and add these to our state container.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">private </span><span style="color:#50FA7B">query</span><span style="color:#F8F8F2">(collection: AngularFirestoreCollection</span><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2">ItemData</span><span style="color:#FF79C6">></span><span style="color:#F8F8F2">): </span><span style="color:#8BE9FD;font-style:italic">Promise</span><span style="color:#FF79C6">&#x3C;void></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  return new </span><span style="color:#50FA7B">Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">void</span><span style="color:#F8F8F2">>((</span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">reject</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">    try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">      this</span><span style="color:#F8F8F2">.findSubscription </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> collection.</span><span style="color:#50FA7B">snapshotChanges</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">pipe</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#50FA7B">        map</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">actions</span><span style="color:#FF79C6"> =></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">          return</span><span style="color:#F8F8F2"> actions.</span><span style="color:#50FA7B">map</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">a</span><span style="color:#FF79C6"> =></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">            const</span><span style="color:#F8F8F2"> data</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> ItemData</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#F8F8F2">                        a.payload.doc.</span><span style="color:#50FA7B">data</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> ItemData</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">            const</span><span style="color:#F8F8F2"> id </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> a.payload.doc.id;</span></span>
<span class="line"><span style="color:#FF79C6">            const</span><span style="color:#F8F8F2"> ref </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> a.payload.doc.ref;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">              id,</span></span>
<span class="line"><span style="color:#F8F8F2">              ref,</span></span>
<span class="line"><span style="color:#F8F8F2">              data</span></span>
<span class="line"><span style="color:#F8F8F2">            };</span></span>
<span class="line"><span style="color:#F8F8F2">          });</span></span>
<span class="line"><span style="color:#F8F8F2">        })</span></span>
<span class="line"><span style="color:#F8F8F2">      ).</span><span style="color:#50FA7B">subscribe</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">items</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Item</span><span style="color:#F8F8F2">[]) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">addItems</span><span style="color:#F8F8F2">(items);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">        resolve</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">      });</span></span>
<span class="line"><span style="color:#F8F8F2">    } catch (e) {</span></span>
<span class="line"><span style="color:#50FA7B">      reject</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">e</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">private </span><span style="color:#50FA7B">addItems</span><span style="color:#F8F8F2">(items: Item[]): </span><span style="color:#8BE9FD;font-style:italic">Promise</span><span style="color:#FF79C6">&#x3C;void></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  return new </span><span style="color:#50FA7B">Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">void</span><span style="color:#F8F8F2">>((</span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    if</span><span style="color:#F8F8F2"> (!</span><span style="color:#FFB86C;font-style:italic">items</span><span style="color:#F8F8F2"> || items.length &#x3C;</span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">      this</span><span style="color:#F8F8F2">.lastPageReached.</span><span style="color:#50FA7B">next</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">true</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">      resolve</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">      return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">    this</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">itemsSubject</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">asObservable</span><span style="color:#F8F8F2">().</span><span style="color:#8BE9FD;font-style:italic">pipe</span><span style="color:#F8F8F2">(</span><span style="color:#8BE9FD;font-style:italic">take</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">))</span></span>
<span class="line"><span style="color:#F8F8F2">                     .</span><span style="color:#8BE9FD;font-style:italic">subscribe</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">currentItems</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Item</span><span style="color:#F8F8F2">[]) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">      this</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">itemsSubject</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">next</span><span style="color:#F8F8F2">(</span><span style="color:#8BE9FD;font-style:italic">currentItems</span><span style="color:#F8F8F2"> !== </span><span style="color:#8BE9FD;font-style:italic">undefined</span><span style="color:#FF79C6"> ?</span></span>
<span class="line"><span style="color:#F8F8F2">            [</span><span style="color:#FF79C6">...</span><span style="color:#8BE9FD;font-style:italic">currentItems</span><span style="color:#F8F8F2">, </span><span style="color:#FF79C6">...</span><span style="color:#8BE9FD;font-style:italic">items</span><span style="color:#F8F8F2">] </span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#FF79C6">...</span><span style="color:#8BE9FD;font-style:italic">items</span><span style="color:#F8F8F2">]);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">      resolve</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><h3>Component: Logic</h3>
<p>Our service is ready to go, we could now create a new component for the presentation and interaction with the user:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">ionic</span><span style="color:#F1FA8C"> g</span><span style="color:#F1FA8C"> component</span><span style="color:#F1FA8C"> feed</span></span></code></pre><p>In this newly created component, we declare the following variables:</p>
<ol>
<li><code>infiniteScroll</code> : a reference to the component scroller to disable it when there will be nothing left to query</li>
<li><code>items$</code> : an observable which will point to our state of data respectively the data we are looking to display</li>
<li><code>loaded</code> : a boolean to display a message when our application is performing the very first query</li>
<li><code>lastPageReachedSub</code> : a subscription to free the observer when we are done</li>
</ol>
<p>Moreover, we are also referencing the service we created previously and we are implementing <code>OnInit</code> , which we are going to implement afterwards, and <code>OnDestroy</code> to unsubscribe our observer.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Component, OnDestroy, OnInit, ViewChild} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {IonInfiniteScroll} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@ionic/angular</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Observable, Subscription} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">rxjs</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {FeedService, Item} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./feed.service</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  selector</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-feed</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  templateUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./feed.component.html</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  styleUrls</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">./feed.component.scss</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> FeedComponent</span><span style="color:#FF79C6"> implements</span><span style="color:#8BE9FD;font-style:italic"> OnInit</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">OnDestroy</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  @</span><span style="color:#50FA7B">ViewChild</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B;font-style:italic">IonInfiniteScroll</span><span style="color:#F8F8F2">, {static</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#F8F8F2">             infiniteScroll</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> IonInfiniteScroll</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  items$</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Observable</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">Item</span><span style="color:#F8F8F2">[]>;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  loaded </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#F8F8F2"> lastPageReachedSub</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Subscription</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  constructor</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">private</span><span style="color:#FFB86C;font-style:italic"> feedService</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> FeedService</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  ngOnDestroy</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.lastPageReachedSub) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">      this</span><span style="color:#F8F8F2">.lastPageReachedSub.</span><span style="color:#50FA7B">unsubscribe</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>To complete our component, we add the following <code>ngOnInit</code> function which will takes care of:</p>
<ol>
<li>Initializing our state observer</li>
<li>Observing the pagination to disable the infinite scroller component when nothing left to be queried</li>
<li>Notifying when something as been loaded at least once</li>
</ol>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">async </span><span style="color:#50FA7B">ngOnInit</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">  this</span><span style="color:#F8F8F2">.items$ </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.feedService.</span><span style="color:#50FA7B">watchItems</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">  this</span><span style="color:#F8F8F2">.lastPageReachedSub </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">      this</span><span style="color:#F8F8F2">.feedService.</span><span style="color:#50FA7B">watchLastPageReached</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">                      .</span><span style="color:#50FA7B">subscribe</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">reached</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">     if</span><span style="color:#F8F8F2"> (reached </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.infiniteScroll) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">       this</span><span style="color:#F8F8F2">.loaded </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">       this</span><span style="color:#F8F8F2">.infiniteScroll.disabled </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">     }</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">  this</span><span style="color:#F8F8F2">.feedService.</span><span style="color:#50FA7B">watchItems</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">pipe</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#50FA7B">      filter</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">flats</span><span style="color:#FF79C6"> =></span><span style="color:#F8F8F2"> flats </span><span style="color:#FF79C6">!==</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">),</span></span>
<span class="line"><span style="color:#50FA7B">      take</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">)).</span><span style="color:#50FA7B">subscribe</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">_items</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Item</span><span style="color:#F8F8F2">[]) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.loaded </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>We add a very last method called <code>findNext</code> which will be triggered by the scroller component when the user reaches the bottom of the page and which will be use to fetch the next data.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">async </span><span style="color:#50FA7B">findNext</span><span style="color:#F8F8F2">($event) {</span></span>
<span class="line"><span style="color:#50FA7B">  setTimeout</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.feedService.</span><span style="color:#50FA7B">find</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    $event.target.</span><span style="color:#50FA7B">complete</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  }, </span><span style="color:#BD93F9">500</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><h3>Component: Rendering</h3>
<p>Our JavaScript code is ready, we can add the HTML implementation of our component.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">ng-container</span><span style="color:#50FA7B;font-style:italic"> *ngIf</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">loaded; else feedLoading;</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">ion-card</span><span style="color:#50FA7B;font-style:italic"> *ngFor</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">let item of (items$ | async);</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">ion-card-header</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">ion-card-title</span><span style="color:#F8F8F2">>{{item.data.title}}&#x3C;/</span><span style="color:#FF79C6">ion-card-title</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#FF79C6">ion-card-header</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">ion-card-content</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">>{{item.data.content}}&#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#FF79C6">ion-card-content</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">ion-card</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">ion-infinite-scroll</span><span style="color:#50FA7B;font-style:italic"> (ionInfinite)</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">findNext($event)</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">ion-infinite-scroll-content</span><span style="color:#F8F8F2">> &#x3C;/</span><span style="color:#FF79C6">ion-infinite-scroll-content</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">ion-infinite-scroll</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">ng-container</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">ng-template</span><span style="color:#50FA7B;font-style:italic"> #feedLoading</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">main</span><span style="color:#F8F8F2">>Initializing your feed...&#x3C;/</span><span style="color:#FF79C6">main</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">ng-template</span><span style="color:#F8F8F2">></span></span></code></pre><p>Finally, we define a minimal height for our cards in the related SCSS file. Without it, the scroller component might never been used as it will only trigger its action if the content of the window has effectively a scroll (“no scroll will happens if nothing to scroll”).</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">ion-card</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD">    min-height</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 1080</span><span style="color:#FF79C6">px</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Voilà, we have implemented an infinite scroller with Ionic, Angular and Firestore 🎉</p>
<h3>Cherry On The Cake 🍒🎂</h3>
<p>The above code is related to Angular but Ionic could be use with or without any modern frameworks. We notably use the same approach in our web open source editor for presentations, <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>, which is developed with Stencil. If you are interested by such solution, have a look at our source code on <a href="https://github.com/deckgo/deckdeckgo" rel="noopener noreferrer external">GitHub</a> or ping me if you want me to share the solution in a new blog post 😁.</p>
<p>To infinity and beyond 🚀</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[DeckDeckGo: Kick-starting 2020 with so much improvements]]></title>
          <description><![CDATA[We are unleashing Google Fonts, private assets, accessibility improvements and many new features in our web open source editor for presentations.]]></description>
          <link>https://daviddalbusco.com/blog/deckdeckgo-kick-starting-2020-with-so-much-improvements/</link>
          <pubDate>Mon, 20 Jan 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*Y4bkN-RtF1ROgb9jnGaE4g.png" alt="" / loading="lazy"><br />
<em>Background photo by <a href="https://unsplash.com/@spacex?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">SpaceX</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>We have been relatively quiet since begin of last December, not on purpose, you know, just life, but recently, we increased our efforts and were even able to put together our biggest release so far! What a way of starting 2020.</p>
<p>Therefore, we are happy to release today the new version of our web open source editor for presentations: <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>.</p>
<p>It contains many improvements, fixes and notably the following features, which we hope, you are going to like.</p>
<h3>Google Fonts</h3>
<p>Even if it was, and still is, already included our <a href="https://docs.deckdeckgo.com" rel="noopener noreferrer external">developer starter kit</a>, we never took the time to add the option to let our users customize easily the font of their decks. As of now, you will be able to use <a href="https://fonts.google.com" rel="noopener noreferrer external">Google Fonts</a> for your presentation.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*U4Uc7u26qI7Seutz1ITlqQ.gif" alt="" / loading="lazy"></p>
<h3>Private Assets For Every Users</h3>
<p>Even though it was actually already the case, we are also really happy to communicate that moreover the content, all custom assets (images or charts data) uploaded in our cloud (powered by the <a href="https://firebase.google.com/docs/storage" rel="noopener noreferrer external">Firebase Storage</a>) are private for every users too.</p>
<p>These are becoming public only once you would share your presentations online.</p>
<p>It might sounds silly to highlight this fact but we invested some times in this particular subject as we worship the principle of “privacy per default”.</p>
<h3>Clone Presentations</h3>
<p>When you develop a platform you often have to develop more than “just” its core process, about which I tend sometimes to be a bit obsessed 😅, but also utilities around it. Once of these which was still missing, was the ability to clone presentations. This has now been solved.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*1CgaF2DzIeI1OK25f8Y-qg.gif" alt="" / loading="lazy"></p>
<h3>Landing Page</h3>
<p>I’m agree that is maybe not the most meaningful feature for our existing users but without “landing page”, we guess that it was probably difficult for anyone landing on DeckDeckGo to actually understand quickly what’s all about, even if it is possible to create a couple of slides without having to register. Something which, by the way, doesn’t seems to be the norm nowadays on the internet and which is really important to us.</p>
<p>It is worth to notice, notably as DeckDeckGo is a Progressive Web Apps and could be used in any mobile devices, that once registered, the “landing page” doesn’t appear anymore and our feed of recently shared presentations becomes again the welcoming screen.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*AHvgqdYhGiNjNFgshMsLyw.gif" alt="" / loading="lazy"></p>
<h3>Accessibility</h3>
<p>We just have one thing to say about accessibility: Roy (<a href="https://twitter.com/donroyco" rel="noopener noreferrer external">Twitter</a> / <a href="https://github.com/donroyco" rel="noopener noreferrer external">GitHub</a>) is a <strong>hero</strong>.</p>
<p>A cold Saturday last December, my inbox began to throw notifications about new GitHub issues which were recently opened, one after the other for quite an amount of time. All had to do with accessibility and moreover all were really well documented and totally correct, not questions ask. All these inputs were provided by Roy. He took some times, out of nowhere, to go through DeckDeckGo in order to test its accessibility. Afterwards he even provided three different GitHub issues’ templates which were missing too.</p>
<p>Really what could I say, if you don’t have faith in humanity and open source, Roy’s story is there to show you the opposite.</p>
<p>Thank you so much Roy 🙏</p>
<p><img src="https://daviddalbusco.com/assets/images/1*mgg66qja7oR8xi9kxtQMdA.gif" alt="" / loading="lazy"></p>
<p>It is also worth to notice that Grant (<a href="https://twitter.com/gherman1990" rel="noopener noreferrer external">Twitter</a> / <a href="https://github.com/grantlouisherman" rel="noopener noreferrer external">GitHub</a>) contributed <strong>again</strong> to one of our release which is super cool. Grant you are a hero too, thank you 🙏</p>
<h3>Embed Code</h3>
<p>The decks you will edit and share with our online editor are as now of embeddable in any web pages. At first I thought that we should solve this with a super complex solution but finally I opted for a standard iFrame solution “à la Youtube”.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*luu29-ONP3EAumNue6opKA.gif" alt="" / loading="lazy"></p>
<h3>Template “Author” New Design</h3>
<p>DeckDeckGo is based on templates. The goal is to create presentations which fit any responsive device screens but also to be able to create these quickly. The template “Author” is a perfect example of this as it is using your registered (optional) data to prefill the slides with your informations (name, description, social links).</p>
<p>In this release, we have improved its layout, have added some options to the editor, like for example being able to opt out and not display your profile picture if you would rather like. We have also fix a long time issue and the slides will now display your social handles and not the name of the platform (“@daviddalbusco” instead of “Twitter”).</p>
<p><img src="https://daviddalbusco.com/assets/images/1*OS2ouBoXzZOFOQzWCRUYkw.gif" alt="" / loading="lazy"></p>
<p>We hope these features are quite intriguing and slick to you. If you have any feedback, don’t hesitate to reach us on <a href="https://join.slack.com/t/deckdeckgo/shared_invite/enQtNzM0NjMwOTc3NTI0LTBlNmFhODNhYmRkMWUxZmU4ZTQ2MDJiNjlmYWZiODNjMDU5OGRjYThlZmZjMTc5YmQ3MzUzMDlhMzk0ZDgzMDY" rel="noopener noreferrer external">Slack</a> or found me on <a href="https://twitter.com/daviddalbusco" rel="noopener noreferrer external">Twitter</a>.</p>
<p>And of course, the more important, if you have to create a presentation soon, go give a try to <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>!</p>
<p>To infinity and beyond,</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Lazy Load YouTube Video <iFrame/>]]></title>
          <description><![CDATA[How to lazy load Youtube Video using the Intersection Observer API.]]></description>
          <link>https://daviddalbusco.com/blog/lazy-load-youtube-video-iframe/</link>
          <pubDate>Thu, 09 Jan 2020 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*IwlNLyd4Db7716sLSeGq_w.jpeg" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@vitreous_macula?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Julia Joppien</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>The <a href="https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API" rel="noopener noreferrer external">Intersection Observer API</a> is often used to lazy load images but did you know that it can be used to defer any types of elements?</p>
<p>This week I developed a new landing page for <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>, our web open source editor for presentations, in which I’ll showcase some video. That’s why, for performance reason, I had to postpone their loading and why also, I’m sharing this new blog post.</p>
<h3>Soundtrack</h3>
<p>In this article we are going to lazy load a music video clip from my hometown friends <a href="https://maxipuchrodeoclub.bandcamp.com" rel="noopener noreferrer external">Maxi Puch Rodeo Club</a>. I could only highly advise you to play the following video in order to stream some great music while reading this blog post 😉</p>
<iframe width="280" height="158" src="https://www.youtube.com/embed/ol0Wz6tqtZA" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe
<br/>
<h3>Getting Started</h3>
<p>I implemented this experiment with <a href="https://reactjs.org/" rel="noopener noreferrer external">React</a> but the concept could be use with or without any frameworks. Before we actually defer the loading of the video, let’s add it to a component (I collected the <code>iframe</code> embedded code using the share action provided by Youtube).</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> React </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> Video</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> style</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">{ display</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">block</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, height</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">2000px</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, background</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">violet</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> }</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                Maxi Puch Rodeo Club</span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF79C6">iframe</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                    width</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">560</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                    height</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">315</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                    src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://www.youtube.com/embed/ol0Wz6tqtZA</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                    frameBorder</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">0</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                    allow</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">accelerometer;</span></span>
<span class="line"><span style="color:#F1FA8C">                           autoplay;</span></span>
<span class="line"><span style="color:#F1FA8C">                           encrypted-media;</span></span>
<span class="line"><span style="color:#F1FA8C">                           gyroscope;</span></span>
<span class="line"><span style="color:#F1FA8C">                           picture-in-picture</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                    allowFullScreen</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                    title</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Maxi Puch Rodeo Club</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">                >&#x3C;/</span><span style="color:#FF79C6">iframe</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> Video;</span></span></code></pre><p>We can now open our browser and check that it is effectively loaded at the same time that our page. You will notice that the Youtube url is loaded even if the video is not displayed.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*MrPtsQS5FB6aF0L-eXncfQ.gif" alt="" / loading="lazy"></p>
<h3>Obfuscate The Video</h3>
<p>We create a new state to display or not our video. Per default, as we don’t want to load it when our page load, we set it to <code>false</code>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> [showVideo, setShowVideo] </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useState</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">false</span><span style="color:#F8F8F2">);</span></span></code></pre><p>To defer the loading of the video, we are going to use the Intersection Observer API. It will detect if the element is (or going to be) visible in the viewport (if we don’t specify another root to observe). As soon as such a visibility is detected, it will triggers an event to let us perform a task, respectively to let us effectively load the video.</p>
<p>That’s why we are also wrapping our element in a container, because we do need an element to observe during the page lifecycle, regardless of the state of our video. Furthermore, we also create a reference to it in order to instantiate our observer later on.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> React, { createRef, useState } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> Video</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> [showVideo, setShowVideo] </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useState</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">false</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> container </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> createRef</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> style</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">{ display</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">block</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, height</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">2000px</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, background</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">violet</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> }</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                Maxi Puch Rodeo Club</span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> ref</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">container</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#FF79C6">                {</span><span style="color:#F8F8F2">showVideo </span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;</span><span style="color:#FF79C6">iframe</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                        width</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">560</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                        height</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">315</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                        src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://www.youtube.com/embed/ol0Wz6tqtZA</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                        frameBorder</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">0</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                        allow</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">accelerometer;</span></span>
<span class="line"><span style="color:#F1FA8C">                           autoplay;</span></span>
<span class="line"><span style="color:#F1FA8C">                           encrypted-media;</span></span>
<span class="line"><span style="color:#F1FA8C">                           gyroscope;</span></span>
<span class="line"><span style="color:#F1FA8C">                           picture-in-picture</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                        allowFullScreen</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                        title</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Maxi Puch Rodeo Club</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">                    >&#x3C;/</span><span style="color:#FF79C6">iframe</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                ) </span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> undefined</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> Video;</span></span></code></pre><p>We can test our app in the browser, as we did previously, and should notice that the video is now neither loaded nor displayed.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*1EPwmYUiUcKyGdsochnN2w.gif" alt="" / loading="lazy"></p>
<h3>Lazy Loading</h3>
<p>Finally we can create our observer. The <code>rootMargin</code> is used to add a bounding box around the element to compute the intersections and <code>threshold</code> indicates at what percentage of the target’s visibility the observer’s callback should be executed.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> videoObserver </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> IntersectionObserver</span><span style="color:#F8F8F2">(onVideoIntersection, {</span></span>
<span class="line"><span style="color:#F8F8F2">    rootMargin</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">100px 0px</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    threshold</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 0.25</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><p>To instruct it to observe our container, we add a <code>useEffect</code> hook which will be executed according the container. Moreover, we also test if the browser do supports the API (which is <a href="https://caniuse.com/#search=intersection%20observer" rel="noopener noreferrer external">supported</a> currently by all modern platforms) and fallback on an “instant” load, if it would not be the case (“Hello darkness IE my old friend” 😅).</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">useEffect</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (window </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">IntersectionObserver</span><span style="color:#E9F284">"</span><span style="color:#FF79C6"> in</span><span style="color:#F8F8F2"> window) {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (container </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> container.current) {</span></span>
<span class="line"><span style="color:#F8F8F2">            videoObserver.</span><span style="color:#50FA7B">observe</span><span style="color:#F8F8F2">(container.current);</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">else</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">        setShowVideo</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">true</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // eslint-disable-next-line react-hooks/exhaustive-deps</span></span>
<span class="line"><span style="color:#F8F8F2">}, [container]);</span></span></code></pre><p>Finally, we declare a function which will be triggered when the container reaches the viewport. We use it to modify our state, in order to display the video, and to <code>disconnect</code> our observer, as we do not need it anymore.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">function</span><span style="color:#50FA7B"> onVideoIntersection</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">entries</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">entries </span><span style="color:#FF79C6">||</span><span style="color:#F8F8F2"> entries.length </span><span style="color:#FF79C6">&#x3C;=</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (entries[</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">].isIntersecting) {</span></span>
<span class="line"><span style="color:#50FA7B">        setShowVideo</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">true</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">        videoObserver.</span><span style="color:#50FA7B">disconnect</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Voilà, that’s it 🎉 We could perform our test again an notice that the video is only loaded when needed respectively when the container appears 😃</p>
<p><img src="https://daviddalbusco.com/assets/images/1*nXaa2aaSs1jTOPN7XEm0xA.gif" alt="" / loading="lazy"></p>
<h3>Going Further</h3>
<p>Lazy loading is great but you might want also to add some custom control to <code>play</code> and <code>pause</code> your video. For that purpose, we can either code it by ourselves, with the <a href="https://developers.google.com/youtube/iframe_api_reference" rel="noopener noreferrer external">YouTube Player API Reference for iframe Embeds</a>, or use one of the many existing libraries, but, DeckDeckGo is open source and we do split our platform in multiple standalone components, therefore guess what? We do share a <a href="https://docs.deckdeckgo.com/components/youtube" rel="noopener noreferrer external">Web Component</a> to embed easily Youtube video in your applications 😊</p>
<p>Let’s install it.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> install</span><span style="color:#F1FA8C"> @deckdeckgo/youtube</span><span style="color:#BD93F9"> --save</span></span></code></pre><p>And load it in our application.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { applyPolyfills, defineCustomElements } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@deckdeckgo/youtube/dist/loader</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">applyPolyfills</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">then</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    defineCustomElements</span><span style="color:#F8F8F2">(window);</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><p>Then, we remove our state to display or not the video, because the Web Component won't load anything until further notice. We replace it with a new function called <code>loadVideo</code> in which we execute the component's method <code>lazyLoadContent</code> which takes care of everything.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> loadVideo</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (container </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> container.current) {</span></span>
<span class="line"><span style="color:#F8F8F2">        container.current.</span><span style="color:#50FA7B">lazyLoadContent</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Finally, we add two buttons, used to call <code>play</code> and <code>pause</code> and we replace our <code>iframe</code> with the component <code>&lt;deckgo-youtube/&gt;</code>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> React, { createRef, useEffect } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { applyPolyfills, defineCustomElements } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@deckdeckgo/youtube/dist/loader</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">applyPolyfills</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">then</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    defineCustomElements</span><span style="color:#F8F8F2">(window);</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> Video</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> container </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> createRef</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> videoObserver </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> IntersectionObserver</span><span style="color:#F8F8F2">(onVideoIntersection, {</span></span>
<span class="line"><span style="color:#F8F8F2">        rootMargin</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">100px 0px</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        threshold</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 0.25</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    useEffect</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (window </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">IntersectionObserver</span><span style="color:#E9F284">"</span><span style="color:#FF79C6"> in</span><span style="color:#F8F8F2"> window) {</span></span>
<span class="line"><span style="color:#FF79C6">            if</span><span style="color:#F8F8F2"> (container </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> container.current) {</span></span>
<span class="line"><span style="color:#F8F8F2">                videoObserver.</span><span style="color:#50FA7B">observe</span><span style="color:#F8F8F2">(container.current);</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"><span style="color:#F8F8F2">        } </span><span style="color:#FF79C6">else</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">            loadVideo</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">        // eslint-disable-next-line react-hooks/exhaustive-deps</span></span>
<span class="line"><span style="color:#F8F8F2">    }, [container]);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    function</span><span style="color:#50FA7B"> onVideoIntersection</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">entries</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">entries </span><span style="color:#FF79C6">||</span><span style="color:#F8F8F2"> entries.length </span><span style="color:#FF79C6">&#x3C;=</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (entries[</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">].isIntersecting) {</span></span>
<span class="line"><span style="color:#50FA7B">            loadVideo</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">            videoObserver.</span><span style="color:#50FA7B">disconnect</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> loadVideo</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (container </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> container.current) {</span></span>
<span class="line"><span style="color:#F8F8F2">            container.current.</span><span style="color:#50FA7B">lazyLoadContent</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> style</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">{ display</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">block</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, height</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">2000px</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, background</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">violet</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> }</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                Maxi Puch Rodeo Club</span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">button</span><span style="color:#50FA7B;font-style:italic"> onClick</span><span style="color:#FF79C6">={async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> container.current.</span><span style="color:#50FA7B">play</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">>Start&#x3C;/</span><span style="color:#FF79C6">button</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">button</span><span style="color:#50FA7B;font-style:italic"> onClick</span><span style="color:#FF79C6">={async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> container.current.</span><span style="color:#50FA7B">pause</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">>Pause&#x3C;/</span><span style="color:#FF79C6">button</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#8BE9FD;font-style:italic">deckgo-youtube</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                ref</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">container</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://www.youtube.com/embed/ol0Wz6tqtZA</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">            >&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">deckgo-youtube</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> Video;</span></span></code></pre><p>We proceed with our final test, notice that the video is lazy loaded, we play with the buttons and we enjoy the awesome music of <a href="https://maxipuchrodeoclub.bandcamp.com/" rel="noopener noreferrer external">Maxi Puch Rodeo Club</a> 🪕🥁🎵👍</p>
<p><img src="https://daviddalbusco.com/assets/images/1*skQICbYpu0Q-x2C6u3TgkA.gif" alt="" / loading="lazy"></p>
<h3>Contribute To Our Project</h3>
<p>Even if it does the job, our component can be improved. I notably think that a smoother transition to display the video would be useful. That’s why I opened a good first issue in our repo on <a href="https://github.com/deckgo/deckdeckgo/issues/570" rel="noopener noreferrer external">GitHub</a>. If you are up to give a hand, your help would be appreciated 🙏.</p>
<h3>Cherry on the Cake 🍒🎂</h3>
<p>Our component <code>@deckdeckgo/youtube</code> is a Web Component developed with <a href="https://stenciljs.com" rel="noopener noreferrer external">Stencil</a> and therefore it could be use in any modern web applications, with or without any frameworks. Moreover, if like me you tend to be a bit “bundlephobic”, it will add to your application, once minified and gzipped, only <a href="https://bundlephobia.com/result?p=@deckdeckgo/youtube@1.0.0-rc.1-2" rel="noopener noreferrer external">198 bytes</a>.</p>
<p>To infinity and beyond 🚀</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Dark And Light Mode: OS Color Scheme And Theme Switcher]]></title>
          <description><![CDATA[How to add a dark and light mode theme switcher to your application which inherits the OS color scheme as default state too.]]></description>
          <link>https://daviddalbusco.com/blog/dark-and-light-mode-os-color-scheme-and-theme-switcher/</link>
          <pubDate>Mon, 30 Dec 2019 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*EqOsVaqdhDXCI2Vwi7YAwg.png" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@sincerelymedia?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Sincerely Media</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>There are a lot of good tutorials out there about the subject “dark mode” but less rarely one which covers the two main goals, in my opinion, of such an implementation in a real application. Respectively, giving the users the ability to switch between themes but also inheriting per default the OS color scheme given by the platform, specially as both Google and Apple began to rollout such modes in their products.</p>
<p>That’s why I’m writing this new blog post to share the solution we have implemented, with the help of our contributors, in our web open source editor for presentations, <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>.</p>
<h3>Credits</h3>
<p>As staten above, we have developed such a feature with the help of our contributors, more precisely with the help of Grant Herman (<a href="https://github.com/grantlouisherman" rel="noopener noreferrer external">GitHub</a> / <a href="https://twitter.com/gherman1990" rel="noopener noreferrer external">Twitter</a>). He even helped us implement it in three different applications 🤯</p>
<p>Definitely, not all heroes wear capes, thank you Grant 🙏</p>
<h3>Getting Started</h3>
<p>To implement our solution, we are relying on two amazing pieces of software:</p>
<ul>
<li><a href="https://github.com/jakearchibald/idb-keyval" rel="noopener noreferrer external">idb-keyval</a>: a super simple small promise-based keyval store implemented with IndexedDB, which we are using to save the user selection.</li>
<li><a href="https://rxjs-dev.firebaseapp.com" rel="noopener noreferrer external">RxJS</a>: a reactive extensions library for JavaScript, which we are using to store and propagate the state representing the theme in memory.</li>
</ul>
<p>We are going to use a runtime value to switch between themes, therefore, it would be also possible to implement such a solution using a <a href="https://react-redux.js.org" rel="noopener noreferrer external">React Redux</a> stored state. Don’t hesitate to ping me if you would like to get such an example, I also have got one ready in another new application I am currently developing 😉</p>
<h3>Singleton Service</h3>
<p>To handle our theme state, we create a Vanilla singleton service. If you are using a framework like <a href="https://angular.io" rel="noopener noreferrer external">Angular</a>, create a root scoped service as you would always do, or if you rather like to use functions and static values, do so. The important thing is to load and keep in memory only one single state representing the applied theme for your all application.</p>
<p>In our service, we declare a boolean <code>darkTheme</code>, which sets to <code>true</code> means “dark theme active” respectively <code>false</code> for “light theme”. We could have used an <code>enum</code>, if more than two themes would have been available. This possibility of expansion is kind of a cool asset of this implementation 😃.</p>
<p>Note that we are using a <code>ReplaySubject&lt;1&gt;</code> as we want to keep in memory exactly one only state without value until we first figure out, what theme to apply.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Observable, ReplaySubject} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">rxjs</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {get, set} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">idb-keyval</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> ThemeService</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    private</span><span style="color:#FF79C6"> static</span><span style="color:#F8F8F2"> instance</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> ThemeService</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    private</span><span style="color:#F8F8F2"> darkTheme</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> ReplaySubject</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">boolean</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#FF79C6;font-weight:bold">                             new</span><span style="color:#50FA7B"> ReplaySubject</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">boolean</span><span style="color:#F8F8F2">>(</span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    private</span><span style="color:#FF79C6"> constructor</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#6272A4">        // Private constructor, singleton</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    static</span><span style="color:#50FA7B"> getInstance</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">ThemeService.instance) {</span></span>
<span class="line"><span style="color:#F8F8F2">            ThemeService.instance </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> ThemeService</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> ThemeService.instance;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><h3>Watch And Switch State</h3>
<p>Our service being ready, we have to expose the state for the application and have to provide a method to let our users be able to toggle the theme. We alsot have to save the selection in order to load it next time the app will restart.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">watch</span><span style="color:#F8F8F2">(): Observable</span><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2">boolean</span><span style="color:#FF79C6">></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    return this.darkTheme.asObservable();</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">async </span><span style="color:#FF79C6">switch</span><span style="color:#F8F8F2">(dark: boolean) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.darkTheme.</span><span style="color:#50FA7B">next</span><span style="color:#F8F8F2">(dark);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        await</span><span style="color:#50FA7B"> set</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">dark_mode</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, dark);</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#F8F8F2">        console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(err);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><h3>Initial Preference</h3>
<p>At boot time, we have to load the theme according the following steps:</p>
<ul>
<li>Does the user already have set a preferred theme?</li>
<li>Or were we unable to read this information (does an error occurred)?</li>
<li>Or should we fallback using the OS default color scheme provided by the platform?</li>
</ul>
<p>For these reasons, we create a function which implements this logic and use our previous <code>switch</code> method to propagate the theme.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">async </span><span style="color:#50FA7B">init</span><span style="color:#F8F8F2">(): </span><span style="color:#8BE9FD;font-style:italic">Promise</span><span style="color:#FF79C6">&#x3C;void></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    try {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> saved</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> get</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">dark_mode</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">        // If user already specified once a preference</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (saved </span><span style="color:#FF79C6">!==</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">            this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">switch</span><span style="color:#F8F8F2">(saved);</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    } catch (err) {</span></span>
<span class="line"><span style="color:#F8F8F2">        this.</span><span style="color:#50FA7B">switch</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">false</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">        return;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // Otherwise we check the prefers-color-scheme of the OS</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> darkFromMedia</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> MediaQueryList</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#F8F8F2">              window.</span><span style="color:#50FA7B">matchMedia</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">(prefers-color-scheme: dark)</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">switch</span><span style="color:#F8F8F2">(darkFromMedia.matches);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><h3>Apply The Theme To The DOM</h3>
<p>On purpose, we did not effectively applied the theme “graphically”, in our service. Therefore, we now have to consume it where we want to apply the modification to the DOM.</p>
<p>In our projects, as we have developed our applications with <a href="https://stenciljs.com" rel="noopener noreferrer external">Stencil</a> Web Components, we have started the initialization in the root component (<code>app-root.tsx)</code>.</p>
<p>Moreover, we are watching for changes in the same component, as it won’t be destroyed until the application is closed. Doing so, on each new state emitted, we modify our DOM, more precisely the <code>body</code> element, to apply or remove a CSS class name (in our case <code>dark</code> ).</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Component, h} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@stencil/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Subscription} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">rxjs</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {ThemeService} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./theme.service</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    tag</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-root</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    styleUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-root.scss</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> AppRoot</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    private</span><span style="color:#F8F8F2"> sub</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Subscription</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">    private</span><span style="color:#F8F8F2"> domBodyClassList</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> DOMTokenList</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#F8F8F2">                              document.body.classList;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    async</span><span style="color:#50FA7B"> componentWillLoad</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        this</span><span style="color:#F8F8F2">.sub </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#F8F8F2">             ThemeService.</span><span style="color:#50FA7B">getInstance</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">                         .</span><span style="color:#50FA7B">watch</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">                         .</span><span style="color:#50FA7B">subscribe</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">dark</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">             this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">updatePreferences</span><span style="color:#F8F8F2">(dark);</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.themeService.</span><span style="color:#50FA7B">init</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    componentDidUnload</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.sub) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">            this</span><span style="color:#F8F8F2">.sub.</span><span style="color:#50FA7B">unsubscribe</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    private</span><span style="color:#50FA7B"> updatePreferences</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">dark</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">        dark </span><span style="color:#FF79C6">?</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">            this</span><span style="color:#F8F8F2">.domBodyClassList.</span><span style="color:#50FA7B">add</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">dark</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">            this</span><span style="color:#F8F8F2">.domBodyClassList.</span><span style="color:#50FA7B">remove</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">dark</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><h3>Theme Switcher</h3>
<p>Until this point was reached, our application was “only” able to handle the OS preferred color scheme and did not gave the users the ability to toggle the themes. That’s why we create a new component which exposes for example an <a href="https://ionicframework.com/docs/api/toggle" rel="noopener noreferrer external">Ionic toggler</a> to switch between light and dark mode.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Component, h, State} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@stencil/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Subscription} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">rxjs</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {ThemeService} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./theme.service</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    tag</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-theme-switcher</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> AppThemeSwitcher</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    private</span><span style="color:#F8F8F2"> sub</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Subscription</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    @</span><span style="color:#50FA7B">State</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#FF79C6">    private</span><span style="color:#F8F8F2"> darkTheme</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    componentWillLoad</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        this</span><span style="color:#F8F8F2">.sub </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> ThemeService.</span><span style="color:#50FA7B">getInstance</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">                         .</span><span style="color:#50FA7B">watch</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">                         .</span><span style="color:#50FA7B">subscribe</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">dark</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">            this</span><span style="color:#F8F8F2">.darkTheme </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> dark;</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    componentDidUnload</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.sub) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">            this</span><span style="color:#F8F8F2">.sub.</span><span style="color:#50FA7B">unsubscribe</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    async</span><span style="color:#50FA7B"> toggleTheme</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        await</span><span style="color:#F8F8F2"> ThemeService.</span><span style="color:#50FA7B">getInstance</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">switch</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">!</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.darkTheme);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-toggle</span><span style="color:#50FA7B;font-style:italic"> checked</span><span style="color:#FF79C6">={</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.darkTheme</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                           onClick</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">toggleTheme</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ion-toggle</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><h3>Styling</h3>
<p>You could either style the themes using CSS variables or even just properties. In both case, what does matter, is applying the theme according the class names we have just set on the <code>body</code> element, respectively <code>dark</code> .</p>
<p>In case you would use Ionic, you would for example be able to style a specific background and text color in your <code>variables.scss</code> ( <code>:root</code> without selector being the light theme):</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6;font-style:italic">:</span><span style="color:#50FA7B;font-style:italic">root</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    --ion-text-color</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> black</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">body</span><span style="color:#50FA7B;font-style:italic">.dark</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    --ion-background-color</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> black</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    --ion-text-color</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> white</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Or another example in plain HTML/CSS:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">style</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#FF79C6">    body</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD">        background</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> red</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">        color</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> yellow</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#FF79C6">    body</span><span style="color:#50FA7B;font-style:italic">.dark</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD">        background</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> purple</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD">        color</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> green</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">style</span><span style="color:#F8F8F2">></span></span></code></pre><h3>All Together</h3>
<p>Our project, DeckDeckGo, is open source 😺. Therefore, let me share with you the above implementation with some GitHub references of our remote control:</p>
<ul>
<li>Singleton service: <a href="https://github.com/deckgo/deckdeckgo/blob/master/remote/src/app/services/theme/theme.service.tsx" rel="noopener noreferrer external">Theme.service</a>.ts</li>
<li>Init and watch: <a href="https://github.com/deckgo/deckdeckgo/blob/master/remote/src/app/app-root.tsx" rel="noopener noreferrer external">app-root.tsx</a></li>
<li>An example of switcher: <a href="https://github.com/deckgo/deckdeckgo/blob/master/remote/src/app/components/settings/app-general-settings/app-general-settings.tsx" rel="noopener noreferrer external">app-general-settings.tsx</a></li>
<li>Styling: <a href="https://github.com/deckgo/deckdeckgo/blob/master/remote/src/global/theme/variables.scss" rel="noopener noreferrer external">variables.scss</a></li>
</ul>
<p><img src="https://daviddalbusco.com/assets/images/1*KgzkIUhche5Netqi1kfSTQ.gif" alt="" / loading="lazy"></p>
<h3>Cherry on the Cake 🍒🎂</h3>
<p>Maybe you are actually not interested to split your code in different layers and just want to have one single Web Component which does take care of everything?</p>
<p>Guess what , we have got such a component too, the one we implemented in our <a href="https://docs.deckdeckgo.com" rel="noopener noreferrer external">documentation</a> for developers 😊</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Component, h, State} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@stencil/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {set, get} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">idb-keyval</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  tag</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-theme-switcher</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> AppThemeSwitcher</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  @</span><span style="color:#50FA7B">State</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#F8F8F2"> darkMode</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#FF79C6"> =</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#F8F8F2"> domBodyClassList</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> DOMTokenList</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> document.body.classList;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#FF79C6"> async</span><span style="color:#50FA7B"> init</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">boolean</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">    try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">      const</span><span style="color:#F8F8F2"> saved</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> get</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">dark_mode</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">      if</span><span style="color:#F8F8F2"> (saved </span><span style="color:#FF79C6">!==</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> saved;</span></span>
<span class="line"><span style="color:#F8F8F2">      }</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#FF79C6">      return</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> darkFromMedia</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> MediaQueryList</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#F8F8F2">              window.</span><span style="color:#50FA7B">matchMedia</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">(prefers-color-scheme: dark)</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> darkFromMedia.matches;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#50FA7B"> update</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    !</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.darkMode </span><span style="color:#FF79C6">?</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">      this</span><span style="color:#F8F8F2">.domBodyClassList.</span><span style="color:#50FA7B">add</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">dark</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">      this</span><span style="color:#F8F8F2">.domBodyClassList.</span><span style="color:#50FA7B">remove</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">dark</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.darkMode </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> !</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.darkMode;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  async</span><span style="color:#50FA7B"> componentWillLoad</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.darkMode </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">init</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.darkMode) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">      this</span><span style="color:#F8F8F2">.domBodyClassList.</span><span style="color:#50FA7B">add</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">dark</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#FF79C6"> async</span><span style="color:#50FA7B"> toggleTheme</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">update</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">      await</span><span style="color:#50FA7B"> set</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">dark_mode</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.darkMode);</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#F8F8F2">      console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(err);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-toggle</span><span style="color:#50FA7B;font-style:italic"> checked</span><span style="color:#FF79C6">={</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.darkMode</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                       onIonChange</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">toggleTheme</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ion-toggle</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>To infinity and beyond 🚀</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Generate Contrasting Text For Your Random Background Color]]></title>
          <description><![CDATA[How to generate automatically a contrasting text for your random background color in Javascript.]]></description>
          <link>https://daviddalbusco.com/blog/generate-contrasting-text-for-your-random-background-color/</link>
          <pubDate>Mon, 23 Dec 2019 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*o8tTGo3vsocTKnCUyz0wHA.jpeg" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@codytdavis?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">davisco</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I’m currently developing a new application to improve, notably, my <a href="https://react-redux.js.org" rel="noopener noreferrer external">React Redux</a> know-how. In this new project, users will be able to assign the color of their choice to some entities background. That’s why I had to find a solution to display contrasted texts.</p>
<h3>Credits</h3>
<p>I was looking to resolve this problem this weekend until I suddenly remembered that the <a href="https://ionicframework.com/docs/theming/colors" rel="noopener noreferrer external">Ionic Color Generator</a> actually already solved it. I had a look at their <a href="https://github.com/ionic-team/ionic-docs/blob/master/src/components/color-gen/color.ts" rel="noopener noreferrer external">code</a> (you gotta love open source ❤️) and based the solution on their implementation.</p>
<p>I also had to implement a function to convert hexadecimal to RGB colors. For that purpose, I found a clean regular expression on <a href="https://stackoverflow.com/a/5624139/5404186" rel="noopener noreferrer external">Stackoverflow</a> provided by <a href="https://stackoverflow.com/users/2404470/xameeramir" rel="noopener noreferrer external">xameeramir</a>.</p>
<h3>Generate A Contrasting Text</h3>
<p>To generate a contrasting text, we are using the following formula defined by the <a href="http://www.w3.org/WAI/ER/WD-AERT/#color-contrast" rel="noopener noreferrer external">world wide web consortium (W3C)</a> to ensure that foreground and background color combinations provide sufficient contrast when viewed by someone having color deficits or when viewed on a black and white screen:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">((Red value X </span><span style="color:#BD93F9">299</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">+</span><span style="color:#F8F8F2"> (Green value X </span><span style="color:#BD93F9">587</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">+</span><span style="color:#F8F8F2"> (Blue value X </span><span style="color:#BD93F9">114</span><span style="color:#F8F8F2">)) </span><span style="color:#FF79C6">/</span><span style="color:#BD93F9"> 1000</span></span></code></pre><p>The above algorithm takes a YIQ color scheme, converted from a RGB formula, as input and outputs a perceived brightness for a color.</p>
<p>Because I’m working with hexadecimal colors, the implementation needs two conversions before being able to calculate the brightness. It first need to convert the the input to RGB and then to YIQ colors.</p>
<p>Finally, with the help of a threshold value, it could determine if the contrast should be dark or light and provide as result a contrasting text “color”, either a black or white.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">interface</span><span style="color:#8BE9FD;font-style:italic"> RGB</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    b</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    g</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    r</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">function</span><span style="color:#50FA7B"> rgbToYIQ</span><span style="color:#F8F8F2">({ </span><span style="color:#FFB86C;font-style:italic">r</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">g</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">b</span><span style="color:#F8F8F2"> }</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> RGB</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> (r </span><span style="color:#FF79C6">*</span><span style="color:#BD93F9"> 299</span><span style="color:#FF79C6"> +</span><span style="color:#F8F8F2"> g </span><span style="color:#FF79C6">*</span><span style="color:#BD93F9"> 587</span><span style="color:#FF79C6"> +</span><span style="color:#F8F8F2"> b </span><span style="color:#FF79C6">*</span><span style="color:#BD93F9"> 114</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">/</span><span style="color:#BD93F9"> 1000</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">function</span><span style="color:#50FA7B"> hexToRgb</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">hex</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> RGB</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">hex </span><span style="color:#FF79C6">||</span><span style="color:#F8F8F2"> hex </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> undefined</span><span style="color:#FF79C6"> ||</span><span style="color:#F8F8F2"> hex </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> ""</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> result</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> RegExpExecArray</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> null</span><span style="color:#FF79C6"> =</span><span style="color:#FF5555"> /</span><span style="color:#FF79C6">^</span><span style="color:#F1FA8C">#</span><span style="color:#FF79C6">?</span><span style="color:#FFB86C">(</span><span style="color:#8BE9FD">[</span><span style="color:#BD93F9">a-f\d</span><span style="color:#8BE9FD">]</span><span style="color:#FF79C6">{2}</span><span style="color:#FFB86C">)(</span><span style="color:#8BE9FD">[</span><span style="color:#BD93F9">a-f\d</span><span style="color:#8BE9FD">]</span><span style="color:#FF79C6">{2}</span><span style="color:#FFB86C">)(</span><span style="color:#8BE9FD">[</span><span style="color:#BD93F9">a-f\d</span><span style="color:#8BE9FD">]</span><span style="color:#FF79C6">{2}</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">$</span><span style="color:#FF5555">/</span><span style="color:#FF79C6">i</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">exec</span><span style="color:#F8F8F2">(hex);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> result</span></span>
<span class="line"><span style="color:#FF79C6">        ?</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">                r</span><span style="color:#FF79C6">:</span><span style="color:#50FA7B"> parseInt</span><span style="color:#F8F8F2">(result[</span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">], </span><span style="color:#BD93F9">16</span><span style="color:#F8F8F2">),</span></span>
<span class="line"><span style="color:#F8F8F2">                g</span><span style="color:#FF79C6">:</span><span style="color:#50FA7B"> parseInt</span><span style="color:#F8F8F2">(result[</span><span style="color:#BD93F9">2</span><span style="color:#F8F8F2">], </span><span style="color:#BD93F9">16</span><span style="color:#F8F8F2">),</span></span>
<span class="line"><span style="color:#F8F8F2">                b</span><span style="color:#FF79C6">:</span><span style="color:#50FA7B"> parseInt</span><span style="color:#F8F8F2">(result[</span><span style="color:#BD93F9">3</span><span style="color:#F8F8F2">], </span><span style="color:#BD93F9">16</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"><span style="color:#FF79C6">        :</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> contrast</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">colorHex</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">threshold</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#FF79C6"> =</span><span style="color:#BD93F9"> 128</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (colorHex </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">#000</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> rgb</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> RGB</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#FF79C6"> =</span><span style="color:#50FA7B"> hexToRgb</span><span style="color:#F8F8F2">(colorHex);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (rgb </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">#000</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#50FA7B"> rgbToYIQ</span><span style="color:#F8F8F2">(rgb) </span><span style="color:#FF79C6">>=</span><span style="color:#F8F8F2"> threshold </span><span style="color:#FF79C6">?</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">#000</span><span style="color:#E9F284">"</span><span style="color:#FF79C6"> :</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">#fff</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><h3>Demo With Vanilla Javascript</h3>
<p>Let’s give a try to the above solution in Vanilla Javascript.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*33JQbJ-KqGrry-VIXHZbMA.gif" alt="" / loading="lazy"></p>
<p><em>Contrasting text automatically generated for the selected color</em></p>
<p>In an <code>html</code> page, we add a color picker to select a dynamic value. For that purpose, we use the component we developed for <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>, our web open source editor for presentations. We load the component from <a href="https://unpkg.com" rel="noopener noreferrer external">Unpkg</a> that’s why no dependencies have to be installed locally.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;!</span><span style="color:#FF79C6">doctype</span><span style="color:#50FA7B;font-style:italic"> html</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">html</span><span style="color:#50FA7B;font-style:italic"> dir</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ltr</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">en</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">head</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">meta</span><span style="color:#50FA7B;font-style:italic"> charset</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">utf-8</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">title</span><span style="color:#F8F8F2">>Contrast color&#x3C;/</span><span style="color:#FF79C6">title</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">script</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            type</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">module</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://unpkg.com/@deckdeckgo/color@latest/dist/deckdeckgo-color/deckdeckgo-color.esm.js</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">        >&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">script</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            nomodule</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">""</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://unpkg.com/@deckdeckgo/color@latest/dist/deckdeckgo-color/deckdeckgo-color.js</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">        >&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">head</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">body</span><span style="color:#50FA7B;font-style:italic"> style</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">background: #F78DA7;</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">h1</span><span style="color:#50FA7B;font-style:italic"> style</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">font-size: 40px;</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Hello World&#x3C;/</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">deckgo-color</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">deckgo-color</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">        &#x3C;!-- Here we will add the contrast function --></span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">        &#x3C;!-- Here we will bind the event and modify the colors --></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">body</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">html</span><span style="color:#F8F8F2">></span></span></code></pre><p>Then we add our above function to generate a contrasting text. Note that we just remove the Typescript part and only parse the Javascript code.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#FF79C6">    function</span><span style="color:#50FA7B"> rgbToYIQ</span><span style="color:#F8F8F2">({ </span><span style="color:#FFB86C;font-style:italic">r</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">g</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">b</span><span style="color:#F8F8F2"> }) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> (r </span><span style="color:#FF79C6">*</span><span style="color:#BD93F9"> 299</span><span style="color:#FF79C6"> +</span><span style="color:#F8F8F2"> g </span><span style="color:#FF79C6">*</span><span style="color:#BD93F9"> 587</span><span style="color:#FF79C6"> +</span><span style="color:#F8F8F2"> b </span><span style="color:#FF79C6">*</span><span style="color:#BD93F9"> 114</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">/</span><span style="color:#BD93F9"> 1000</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    function</span><span style="color:#50FA7B"> hexToRgb</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">hex</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">hex </span><span style="color:#FF79C6">||</span><span style="color:#F8F8F2"> hex </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> undefined</span><span style="color:#FF79C6"> ||</span><span style="color:#F8F8F2"> hex </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> ""</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> result </span><span style="color:#FF79C6">=</span><span style="color:#FF5555"> /</span><span style="color:#FF79C6">^</span><span style="color:#F1FA8C">#</span><span style="color:#FF79C6">?</span><span style="color:#FFB86C">(</span><span style="color:#8BE9FD">[</span><span style="color:#BD93F9">a-f\d</span><span style="color:#8BE9FD">]</span><span style="color:#FF79C6">{2}</span><span style="color:#FFB86C">)(</span><span style="color:#8BE9FD">[</span><span style="color:#BD93F9">a-f\d</span><span style="color:#8BE9FD">]</span><span style="color:#FF79C6">{2}</span><span style="color:#FFB86C">)(</span><span style="color:#8BE9FD">[</span><span style="color:#BD93F9">a-f\d</span><span style="color:#8BE9FD">]</span><span style="color:#FF79C6">{2}</span><span style="color:#FFB86C">)</span><span style="color:#FF79C6">$</span><span style="color:#FF5555">/</span><span style="color:#FF79C6">i</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">exec</span><span style="color:#F8F8F2">(hex);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> result</span></span>
<span class="line"><span style="color:#FF79C6">            ?</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">                    r</span><span style="color:#FF79C6">:</span><span style="color:#50FA7B"> parseInt</span><span style="color:#F8F8F2">(result[</span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">], </span><span style="color:#BD93F9">16</span><span style="color:#F8F8F2">),</span></span>
<span class="line"><span style="color:#F8F8F2">                    g</span><span style="color:#FF79C6">:</span><span style="color:#50FA7B"> parseInt</span><span style="color:#F8F8F2">(result[</span><span style="color:#BD93F9">2</span><span style="color:#F8F8F2">], </span><span style="color:#BD93F9">16</span><span style="color:#F8F8F2">),</span></span>
<span class="line"><span style="color:#F8F8F2">                    b</span><span style="color:#FF79C6">:</span><span style="color:#50FA7B"> parseInt</span><span style="color:#F8F8F2">(result[</span><span style="color:#BD93F9">3</span><span style="color:#F8F8F2">], </span><span style="color:#BD93F9">16</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">                }</span></span>
<span class="line"><span style="color:#FF79C6">            :</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    function</span><span style="color:#50FA7B"> contrast</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">colorHex</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">threshold</span><span style="color:#FF79C6"> =</span><span style="color:#BD93F9"> 128</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (colorHex </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">#000</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> rgb </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> hexToRgb</span><span style="color:#F8F8F2">(colorHex);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (rgb </span><span style="color:#FF79C6">===</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">#000</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#50FA7B"> rgbToYIQ</span><span style="color:#F8F8F2">(rgb) </span><span style="color:#FF79C6">>=</span><span style="color:#F8F8F2"> threshold </span><span style="color:#FF79C6">?</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">#000</span><span style="color:#E9F284">"</span><span style="color:#FF79C6"> :</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">#fff</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span></code></pre><p>Finally, we bind an event to the color picker to listen to the selected color, which we apply to the page background and which we use to generate a contrasted value we ultimately set as color of the text of the page.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    document.</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">deckgo-color</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">).</span><span style="color:#50FA7B">addEventListener</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">colorChange</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, updateColor, </span><span style="color:#BD93F9">true</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    function</span><span style="color:#50FA7B"> updateColor</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">$event</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">        document.</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">body</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">).style.background </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> $event.detail.hex;</span></span>
<span class="line"><span style="color:#F8F8F2">        document.</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">body</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">).style.color </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> contrast</span><span style="color:#F8F8F2">($event.detail.hex);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span></code></pre><h3>Cherry on the cake 🍒🎂</h3>
<p>Our <code>@deckdeckgo/color</code> Web Component is open source. It’s a <a href="https://stenciljs.com" rel="noopener noreferrer external">StencilJS</a> Web Component and therefore it can be use in Vanilla Javascript or with any modern frameworks. It is also relatively lightweight, according <a href="https://bundlephobia.com/result?p=@deckdeckgo/color@1.0.0-rc.3-2" rel="noopener noreferrer external">Bundlephobia</a> it only adds 418 Bytes (once minified and gzipped) to any bundles. If you want to give it a try in your application, check its <a href="https://docs.deckdeckgo.com/components/color" rel="noopener noreferrer external">documentation</a> and go for it!</p>
<p>To infinity and beyond, merry Xmas 🎄🚀</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Stylish Cards And Syntax Highlighting With Gatsby]]></title>
          <description><![CDATA[How to display the code of your Gatsby website and blog with styled cards and code highlighting with a new plugin]]></description>
          <link>https://daviddalbusco.com/blog/stylish-cards-and-syntax-highlighting-with-gatsby/</link>
          <pubDate>Tue, 17 Dec 2019 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*8DbEBAv_L1pK-1Xm03XEzw.png" alt="" / loading="lazy"></p>
<p>I am a big fan of <a href="https://carbon.now.sh" rel="noopener noreferrer external">Carbon</a>! I often use it to share tips or mistakes I did on my <a href="https://twitter.com/daviddalbusco" rel="noopener noreferrer external">Twitter</a> feed.</p>
<p>The other day, while I was cooking dinner, the idea suddenly hit me: why not improving our <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a> Web Component to highlight code, which use <a href="https://prismjs.com" rel="noopener noreferrer external">PrismJS</a> under the hood, to display per default such stylish cards instead of “just” displaying naked elements.</p>
<p>I ate my dinner and implemented this idea the same night. I woke up the following days and was still pleased with the result. Therefore I even decided to add it to my personal website developed with <a href="https://www.gatsbyjs.org" rel="noopener noreferrer external">Gatsby</a>.</p>
<p>That’s why I’m happy to introduce <a href="https://github.com/deckgo/gatsby-remark-highlight-code" rel="noopener noreferrer external">gatsby-remark-highlight-code</a>, a new Gatsby plugin to add stylish cards and syntax highlighting to code blocks in markdown files.</p>
<h3>Features</h3>
<p>The main advantages, in my opinion, of this new plugin are the followings:</p>
<ol>
<li>Use PrismJS to highlight code and load at runtime any languages which are supported by the library. Nothing to develop or install, the component does the job at runtime.</li>
<li>Many styling options through CSS4 variables (see the <a href="https://docs.deckdeckgo.com/components/code" rel="noopener noreferrer external">documentation</a> for the extensive list).</li>
<li>Not yet implemented in the plugin, but the component can also display automatically line numbers or highlight selected rows. These features can be added relatively quickly if you would need these.</li>
<li>Finally, the plugin use our Web Component developed with <a href="https://stenciljs.com" rel="noopener noreferrer external">StencilJS</a>. Therefore, if you are please with the result, you could reuse it easily in any modern web applications regardless of the framework (or none) you are using!</li>
</ol>
<h3>Getting Started</h3>
<p>This plugin can be added in the three following steps:</p>
<h4>Installation</h4>
<p>To install the plugin and the component, run the following command in a terminal:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> install</span><span style="color:#F1FA8C"> gatsby-transformer-remark</span><span style="color:#F1FA8C"> gatsby-remark-highlight-code</span><span style="color:#F1FA8C"> @deckdeckgo/highlight-code</span><span style="color:#BD93F9"> --save</span></span></code></pre><h4>Configuration</h4>
<p>In your <code>gatsby-config.js</code> configure (or add) <code>gatsby-transformer-remark</code>:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">plugins: [</span></span>
<span class="line"><span style="color:#F8F8F2">    {</span></span>
<span class="line"><span style="color:#F8F8F2">        resolve</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> `gatsby-transformer-remark`</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        options</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">            plugins</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#F8F8F2">                {</span></span>
<span class="line"><span style="color:#F8F8F2">                    resolve</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> `gatsby-remark-highlight-code`</span></span>
<span class="line"><span style="color:#F8F8F2">                }</span></span>
<span class="line"><span style="color:#F8F8F2">            ]</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">];</span></span></code></pre><h4>Load the component</h4>
<p>Stencil components should currently be loaded at runtime in Gatsby (see issue <a href="https://github.com/ionic-team/stencil/issues/1724" rel="noopener noreferrer external">#1724</a> which should be soon finally solved as I heard in a Stencil chat recently).</p>
<p>For that purpose, load the <a href="https://twitter.com/deckdeckgo/highlight-code" rel="noopener noreferrer external">@deckdeckgo/highlight-code</a> once in one of your pages or components are mounted.</p>
<p>For example add the following in the main file of your website, in your <code>index.js</code>, or in the template of your blog or simply load it where you need it.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">async </span><span style="color:#50FA7B">componentDidMount</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">  try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> deckdeckgoLoader </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#50FA7B">      require</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">@deckdeckgo/highlight-code/dist/loader</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#F8F8F2"> deckdeckgoLoader.</span><span style="color:#50FA7B">defineCustomElements</span><span style="color:#F8F8F2">(window);</span></span>
<span class="line"><span style="color:#F8F8F2">  } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#F8F8F2">    console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(err);</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>That's it, the plugin is configured and ready. After your next build, the code of your blog will be display in stylish cards with syntax highlighting 🎉</p>
<h3>Cherry on the cake 🍒🎂</h3>
<p>As I explained above, the exact same Web Component can be used in any modern web applications and is, of course, open source. But the other cherry on the cake is the fact that we are using it in our open source web editor for presentations and developer kit.</p>
<p>Therefore, if you are going to showcase code in an upcoming talk, give a try to <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a> 😃</p>
<p><img src="https://daviddalbusco.com/assets/images/1*8SuFF5R9siRlSqAkTSXWjQ.gif" alt="" / loading="lazy"></p>
<p>To infinity and beyond 🚀</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[How to declare and pass parameters to an Ionic + React Modal]]></title>
          <description><![CDATA[How to declare and pass parameters to an Ionic + React Modal]]></description>
          <link>https://daviddalbusco.com/blog/how-to-declare-and-pass-parameters-to-an-ionic-react-modal/</link>
          <pubDate>Tue, 10 Dec 2019 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*lTN15RS0mfGtXUAQF5RVJQ.jpeg" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@the_roaming_platypus?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">timJ</a> on <a href="https://unsplash.com/s/photos/dialog?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I’m having fun with <a href="https://ionicframework.com/docs/react" rel="noopener noreferrer external">Ionic React</a> these days and therefore experimenting different components. One of these, which I use almost without exceptions in any applications, is the modal. Although its dedicated <a href="https://ionicframework.com/docs/api/modal" rel="noopener noreferrer external">documentation</a> is pretty neat, I went a bit further, as I like to declare them in their own separate components. That’s why I’m writing this new blog post.</p>
<h3>Getting Started</h3>
<p>To add a modal to an application, we proceed as displayed in the documentation (told you, it is well documented). We use the component <code>IonModal</code> and, in order to trigger its opening and closing, we also use a state (with the help of a <code>useState</code> hook) to modify its property <code>isOpen</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> React, {useState} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {IonModal, IonButton, IonContent} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@ionic/react</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> Tab1</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> React</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">FC</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> [showModal, setShowModal] </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useState</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">false</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonContent</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonModal</span><span style="color:#50FA7B;font-style:italic"> isOpen</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">showModal</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">>This is the modal content.&#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonButton</span><span style="color:#50FA7B;font-style:italic"> onClick</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> setShowModal</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">false</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            Close Modal</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonButton</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonModal</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonButton</span><span style="color:#50FA7B;font-style:italic"> onClick</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> setShowModal</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">true</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            Show Modal</span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonButton</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonContent</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  );</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> Tab1;</span></span></code></pre><p><em>Note that I have used the <em><code>tab</code></em> starter kit to develop this article, that’s why the above page’s name is <em><code>Tab1</code></em> .</em></p>
<h3>Create A Component</h3>
<p>Modals could quickly become as complicated as pages, that’s why, I am used to declare them in their own components. Let’s then try to create a new one in a separate new file, called for example <code>MyModal.tsx</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> React </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { IonHeader, IonContent, IonToolbar, IonTitle } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@ionic/react</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">class</span><span style="color:#8BE9FD"> MyModal</span><span style="color:#FF79C6"> extends</span><span style="color:#8BE9FD;font-style:italic"> React</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">Component</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonHeader</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonToolbar</span><span style="color:#50FA7B;font-style:italic"> color</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">primary</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonTitle</span><span style="color:#F8F8F2">>My Modal&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonTitle</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonToolbar</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonHeader</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonContent</span><span style="color:#50FA7B;font-style:italic"> className</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ion-padding</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">>This is the modal content.&#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonContent</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/></span></span>
<span class="line"><span style="color:#F8F8F2">        );</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> MyModal;</span></span></code></pre><p>Once we have created it, we could use it in our page to replace the previous content of the modal.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> React, { useState } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { IonModal, IonButton, IonContent} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@ionic/react</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> MyModal </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./MyModal</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> Tab1</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> React</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">FC</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> [showModal, setShowModal] </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useState</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">false</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonContent</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonModal</span><span style="color:#50FA7B;font-style:italic"> isOpen</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">showModal</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">MyModal</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">MyModal</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonButton</span><span style="color:#50FA7B;font-style:italic"> onClick</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> setShowModal</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">false</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            Close Modal</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonButton</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonModal</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonButton</span><span style="color:#50FA7B;font-style:italic"> onClick</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> setShowModal</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">true</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            Show Modal</span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonButton</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonContent</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  );</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> Tab1;</span></span></code></pre><h3>To Close The Modal</h3>
<p>Super, we achieved the first step, we have now a modal declared in a separate component. But, in our above example, the action to close the modal, respectively the <code>IonButton</code> button which sets the display state to <code>false</code>, is still rendered outside of our component which is a bit unfortunate in term of design, as, I think, it’s quite common to render such an action in the header of the modal itself.</p>
<p>In order to move this button into the modal, I actually found two possible solutions. One with the use of a <code>callback</code> , probably the cleanest one, and another one using <code>references</code>.</p>
<p>There might be more and I would be really happy to hear about them. Therefore please, ping me with your comments and thank you in advance for your shares 👋</p>
<h4>Callback</h4>
<p>In this solution, we want to pass a callback to the component to close the modal. We enhance it with a new property, which we also use in our header to add the related button.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> React </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {IonHeader, IonContent, IonToolbar, IonTitle, IonButtons, IonButton, IonIcon} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@ionic/react</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">type</span><span style="color:#8BE9FD;font-style:italic"> MyModalProps</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">  closeAction</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Function</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">class</span><span style="color:#8BE9FD"> MyModal</span><span style="color:#FF79C6"> extends</span><span style="color:#8BE9FD;font-style:italic"> React</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">Component</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">MyModalProps</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> &#x3C;></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonHeader</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonToolbar</span><span style="color:#50FA7B;font-style:italic"> color</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">primary</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">          &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonTitle</span><span style="color:#F8F8F2">>My Modal&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonTitle</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">          &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonButtons</span><span style="color:#50FA7B;font-style:italic"> slot</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">end</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonButton</span><span style="color:#50FA7B;font-style:italic"> onClick</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.props.</span><span style="color:#50FA7B">closeAction</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">              &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonIcon</span><span style="color:#50FA7B;font-style:italic"> name</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">close</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> slot</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">icon-only</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonIcon</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonButton</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">          &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonButtons</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonToolbar</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonHeader</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonContent</span><span style="color:#50FA7B;font-style:italic"> className</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ion-padding</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">>This is the modal content.&#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonContent</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/></span></span>
<span class="line"><span style="color:#F8F8F2">  };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> ({</span><span style="color:#FFB86C;font-style:italic">closeAction</span><span style="color:#F8F8F2">}</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { </span><span style="color:#50FA7B">closeAction</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Function</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;</span><span style="color:#8BE9FD;font-style:italic">MyModal</span><span style="color:#50FA7B;font-style:italic"> closeAction</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">closeAction</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">MyModal</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">)</span></span></code></pre><p>Once the component modified, we could create a new function (in our page) to set the display state to <code>false</code> and pass it as callback to our component.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> React, {useState} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {IonModal, IonButton, IonContent} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@ionic/react</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> MyModal </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./MyModal</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> Tab1</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> React</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">FC</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> [showModal, setShowModal] </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useState</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">false</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> closeModal</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#50FA7B"> setShowModal</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">false</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonContent</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonModal</span><span style="color:#50FA7B;font-style:italic"> isOpen</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">showModal</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">MyModal</span><span style="color:#50FA7B;font-style:italic"> closeAction</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">closeModal</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">MyModal</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonModal</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonButton</span><span style="color:#50FA7B;font-style:italic"> onClick</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> setShowModal</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">true</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        Show Modal</span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonButton</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonContent</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  );</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> Tab1;</span></span></code></pre><h4>References</h4>
<p>Another possible solution could be the usage of a DOM reference to dismiss the modal.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> React, {RefObject} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {IonHeader, IonContent, IonToolbar, IonTitle, IonButtons, IonButton, IonIcon} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@ionic/react</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">class</span><span style="color:#8BE9FD"> MyModal</span><span style="color:#FF79C6"> extends</span><span style="color:#8BE9FD;font-style:italic"> React</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">Component</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  headerRef</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> RefObject</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">HTMLIonHeaderElement</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> React.</span><span style="color:#50FA7B">createRef</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  async</span><span style="color:#50FA7B"> closeModal</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.headerRef </span><span style="color:#FF79C6">||</span><span style="color:#FF79C6"> !</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.headerRef.current) {</span></span>
<span class="line"><span style="color:#FF79C6">      return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#F8F8F2"> (</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.headerRef.current.</span><span style="color:#50FA7B">closest</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">ion-modal</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">as</span></span>
<span class="line"><span style="color:#F8F8F2">                HTMLIonModalElement).</span><span style="color:#50FA7B">dismiss</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> &#x3C;></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonHeader</span><span style="color:#50FA7B;font-style:italic"> ref</span><span style="color:#FF79C6">={</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.headerRef</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonToolbar</span><span style="color:#50FA7B;font-style:italic"> color</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">primary</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">          &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonTitle</span><span style="color:#F8F8F2">>My Modal&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonTitle</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">          &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonButtons</span><span style="color:#50FA7B;font-style:italic"> slot</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">end</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonButton</span><span style="color:#50FA7B;font-style:italic"> onClick</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">closeModal</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">              &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonIcon</span><span style="color:#50FA7B;font-style:italic"> name</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">close</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> slot</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">icon-only</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonIcon</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonButton</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">          &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonButtons</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonToolbar</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonHeader</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonContent</span><span style="color:#50FA7B;font-style:italic"> className</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ion-padding</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">>This is the modal content 3.&#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonContent</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/></span></span>
<span class="line"><span style="color:#F8F8F2">  };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> MyModal;</span></span></code></pre><p>The above method has for effect that our <code>state</code> , used in our page for the display purpose, might end up not being synced anymore with the effective state of the modal, as we closed it using the DOM. To overcome this situation, we could sync the information after the dialog has been dismissed.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> React, {useState} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {IonModal, IonButton, IonContent} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@ionic/react</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> MyModal </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./MyModal</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> Tab1</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> React</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">FC</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> [showModal, setShowModal] </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useState</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">false</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonContent</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonModal</span><span style="color:#50FA7B;font-style:italic"> isOpen</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">showModal</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">        onDidDismiss</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> setShowModal</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">false</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">MyModal</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">MyModal</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonModal</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonButton</span><span style="color:#50FA7B;font-style:italic"> onClick</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> setShowModal</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">true</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        Show Modal</span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonButton</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonContent</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  );</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> Tab1;</span></span></code></pre><p>But unfortunately this method has a drawback. As we are modifying the state to synchronize it, our component is going to be “rerendered”. Therefore it is a bit less performant than the solution with <code>callback</code> and that’s why I found this first solution cleaner.</p>
<p><em>Side note: I spent several hours yesterday evening trying without success to wrap <code>shouldComponentUpdate</code> respectively <code>React.Memo</code> around the modal component in order to not render the page again when the state is modified after the modal is dismissed. It is probably possible and again I’ll be happy to hear any tips about this too 😉</em></p>
<h4>Post Publication Update</h4>
<p><a href="https://twitter.com/elylucas" rel="noopener noreferrer external">Ely Lucas</a> thankfully <a href="https://dev.to/elylucas/comment/imio" rel="noopener noreferrer external">answered</a> this blog post with a valid and interesting comment: modal could actually be dismissed without using the button. For example by hitting the Esc key or clicking on the backdrop.</p>
<p>Therefore, a handler to <code>onDidDismiss</code> does have in any case to be defined to sync the open state (as we did in the previous chapter) after the dialog would have been closed.</p>
<h4>Summarized</h4>
<p>Closing the modal with a button through a <code>callback</code> or <code>reference</code> are both possible.</p>
<p>On the other side, listening to <code>onDidDismiss</code> in order to set the state to <code>false</code>, even if it triggers a rerendering, is kind of mandatory.</p>
<h3>To Pass Parameters</h3>
<p>In the previous examples, we already used a property to pass a callback to close the modal. Likewise, we could use the same approach to define any other properties.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> React </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {IonHeader, IonContent, IonToolbar, IonTitle, IonButtons, IonButton, IonIcon} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@ionic/react</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">type</span><span style="color:#8BE9FD;font-style:italic"> MyModalProps</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">  closeAction</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Function</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  text</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">class</span><span style="color:#8BE9FD"> MyModal</span><span style="color:#FF79C6"> extends</span><span style="color:#8BE9FD;font-style:italic"> React</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">Component</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">MyModalProps</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#50FA7B">  render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> &#x3C;></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonHeader</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonToolbar</span><span style="color:#50FA7B;font-style:italic"> color</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">primary</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">          &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonTitle</span><span style="color:#F8F8F2">>My Modal&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonTitle</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">          &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonButtons</span><span style="color:#50FA7B;font-style:italic"> slot</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">end</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonButton</span><span style="color:#50FA7B;font-style:italic"> onClick</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.props.</span><span style="color:#50FA7B">closeAction</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">              &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonIcon</span><span style="color:#50FA7B;font-style:italic"> name</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">close</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> slot</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">icon-only</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonIcon</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonButton</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">          &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonButtons</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonToolbar</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonHeader</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonContent</span><span style="color:#50FA7B;font-style:italic"> className</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ion-padding</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span><span style="color:#FF79C6">{</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.props.text</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonContent</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/></span></span>
<span class="line"><span style="color:#F8F8F2">  };</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> ({</span><span style="color:#FFB86C;font-style:italic">closeAction</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">text</span><span style="color:#F8F8F2">}</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> { </span><span style="color:#50FA7B">closeAction</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Function</span><span style="color:#F8F8F2">, text</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2"> }) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;</span><span style="color:#8BE9FD;font-style:italic">MyModal</span><span style="color:#50FA7B;font-style:italic"> closeAction</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">closeAction</span><span style="color:#FF79C6">}</span><span style="color:#50FA7B;font-style:italic"> text</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">text</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">MyModal</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">)</span></span></code></pre><p>And therefore pass any other parameters from the page to our modal component.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> React, {useState} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {IonModal, IonButton, IonContent} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@ionic/react</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> MyModal </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./MyModal</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#50FA7B"> Tab1</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> React</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">FC</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> [showModal, setShowModal] </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useState</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">false</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> closeModal</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#50FA7B"> setShowModal</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">false</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonContent</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonModal</span><span style="color:#50FA7B;font-style:italic"> isOpen</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">showModal</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">MyModal</span><span style="color:#50FA7B;font-style:italic"> closeAction</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">closeModal</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                 text</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">This is the updated modal content.</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">MyModal</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonModal</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonButton</span><span style="color:#50FA7B;font-style:italic"> onClick</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> setShowModal</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">true</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        Show Modal</span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonButton</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonContent</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  );</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> Tab1;</span></span></code></pre><p>To infinity and beyond 🚀</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Interact With Your Audience With A Live Poll]]></title>
          <description><![CDATA[We are introducing a new feature to let your interact with your audience without leaving your presentation]]></description>
          <link>https://daviddalbusco.com/blog/interact-with-your-audience-with-a-live-poll/</link>
          <pubDate>Thu, 05 Dec 2019 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*ULPgfU4_6DDEHDLDCdUMKQ.jpeg" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@nickxshotz?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Nicholas Green</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>We had this idea for a long time and it took us a bit of time to realize it, that’s why we are so excited to introduce today the new feature of <a href="https://deckdeckgo.com/" rel="noopener noreferrer external">DeckDeckGo</a>, our web open-source editor for presentations, to make possible the interaction with your audience through live polling without having to leave your slides 🔥</p>
<iframe width="280" height="158" src="https://www.youtube.com/embed/GuF58XBzTj0" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe
<br/>
<h3>User Experience</h3>
<p>UX could often be discussed and I have to say that, for that feature in particular, many solutions are feasible. I scratched my head for days, filling papers and taking notes until I finally figured out the core principles of the solution which were meaningful to me and hopefully to others too:</p>
<ul>
<li><p>An audience could be a crowd, a small or large. In case of live polling, not all participants are on the same page. Some might be fast to vote, some might be slow. Some people are near the projected slides or screen, others are probably far away. Some might even be asleep, some might have their phones in their bags, etc.</p></li>
<li><p>As a speaker I find convenient when I don’t have to switch between multiple applications -- it could even to some extent makes me a bit less stressed. As an attendee, I tend to think that it's easier to follow a presentation without too many media interruptions. To sum it up, I like when the presentation’s flow remains as smooth as possible.</p></li>
</ul>
<p>That’s why, at least in this first version of the feature, all the information is available at the same time and any time. Question, answers, connection information and even results of the vote, which are updated in real time, are all displayed on the same slide. Moreover, as I do think that QR codes are the best way to share links with the participants, the template also contains a deep link to point them directly to the answers on their phones.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*6DlXoX4G2WZer9xVYymqug.gif" alt="" / loading="lazy"></p>
<h3>Developer Mindset</h3>
<p>Since day one we always kept a developer mindset. That’s why both the editor and our developer starter kit are using the exact same core, the same web components as engine. Needless to say that it didn’t change. This new feature isn’t just available in our editor but also available with our starter kit. If you would rather like to develop your presentation using HTML or Markdown, it’s all cool and you have access to the exact same feature.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">deckgo-deck</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;</span><span style="color:#FF79C6">deckgo-slide-poll</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">h1</span><span style="color:#50FA7B;font-style:italic"> slot</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">question</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Do you like my presentation?&#x3C;/</span><span style="color:#FF79C6">h2</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#50FA7B;font-style:italic"> slot</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">answer-1</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Sure cool&#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#50FA7B;font-style:italic"> slot</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">answer-2</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>No&#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#50FA7B;font-style:italic"> slot</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">answer-3</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Potatoes&#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#50FA7B;font-style:italic"> slot</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">how-to</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Go to &#x3C;</span><span style="color:#FF79C6">a</span><span style="color:#50FA7B;font-style:italic"> href</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://deckdeckgo.com/poll</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>deckdeckgo.com/poll&#x3C;/</span><span style="color:#FF79C6">a</span><span style="color:#F8F8F2">> and use the code {0}&#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#50FA7B;font-style:italic"> slot</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">awaiting-votes</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Awaiting first votes&#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;/</span><span style="color:#FF79C6">deckgo-slide-poll</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">deckgo-deck</span><span style="color:#F8F8F2">></span></span></code></pre><h3>An Unlimited Amount Of Possibilities</h3>
<p>Of course, the above goals have an impact on our technical decisions but we have designed our architecture to be relatively flexible and to be able to handle such scenarios. And that’s probably even the technical beauty of DeckDeckGo. It is template based with the goal to make them self-contained.</p>
<p>Each template is an independent Web Component developed with <a href="https://stenciljs.com" rel="noopener noreferrer external">StencilJS</a>, therefore each of them are basically the Web. And what could you do with the Web? Anything 🤯</p>
<p>You want to create a template for live polling with DeckDeckGo? That’s possible.</p>
<p>You want to create a template which integrates a space invaders game? That’s possible too.</p>
<p>You want to create a series of reusable templates with shiny designer effects? Of course you could.</p>
<p>Or you even want to create a template to load some Web Assembly code to run directly in your presentation the new prototype of your blockchain AI powered startup for slow food? That’s possible too.</p>
<p>It is just an unlimited amount of possibilities and it is only the beginning!</p>
<h3>Get Started Now</h3>
<p>Get started now to create your next presentation: <a href="https://deckdeckgo.com/" rel="noopener noreferrer external">https://deckdeckgo.com</a>.</p>
<p>To infinity and beyond 🚀</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Infinite Scroll with Ionic + React]]></title>
          <description><![CDATA[How to implement an Infinite Scroll with Ionic + React and a filter with Hooks]]></description>
          <link>https://daviddalbusco.com/blog/infinite-scroll-with-ionic-react/</link>
          <pubDate>Mon, 02 Dec 2019 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*VST3XVmoX1R3hAXJ8OGw4A.jpeg" alt="" / loading="lazy"></p>
<p>Ionic React has been made <a href="https://ionicframework.com/blog/announcing-ionic-react/" rel="noopener noreferrer external">available</a> earlier this year and I’ll introduce some of its features at our local <a href="https://www.meetup.com/fr-FR/Ionic-Zurich/events/265767496/" rel="noopener noreferrer external">meetup</a> this Thursday. One of these is the implementation of an infinite scroller, which I’m about to share with you in the following post 😃</p>
<h3>Before We Start</h3>
<p>To implement the solution we will need an Ionic + React application. If you don’t have one yet or if you just want to create a sample one to follow this tutorial, have a look to the well documented and easy to follow official <a href="https://ionicframework.com/docs/react" rel="noopener noreferrer external">documentation</a> or just kick start one with the following command:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">ionic</span><span style="color:#F1FA8C"> start</span><span style="color:#F1FA8C"> infiniteScroll</span><span style="color:#F1FA8C"> tabs</span><span style="color:#BD93F9"> --type</span><span style="color:#F1FA8C"> react</span></span></code></pre><h3>Getting Started</h3>
<p>The starter kit used by the Ionic to create a new application contains three tabs, three pages, which are themselves React functional components. For convenience reason, we are going to implement our solution in the first tab, respectively in the file <code>./src/pages/Tab1.tsx</code> .</p>
<p>As we are going to implement an Infinite Scroll and therefore going to display a list, the first thing we are going to do, is adding a stateful value which should contains our items. For that purpose we are using the <code>useState</code> React Hook. Moreover we are rendering them using cards and are adding another state which will help us stop the scroller when there will be nothing left to iterate.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  IonContent, IonHeader, IonPage,</span></span>
<span class="line"><span style="color:#F8F8F2">  IonTitle, IonCard, IonToolbar} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@ionic/react</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> React, {useState} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> Tab1</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> React</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">FC</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> [items, setItems] </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useState</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">[]>([]);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> [disableInfiniteScroll, setDisableInfiniteScroll] </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#50FA7B">        useState</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">boolean</span><span style="color:#F8F8F2">>(</span><span style="color:#BD93F9">false</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonPage</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonHeader</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonToolbar</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">          &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonTitle</span><span style="color:#F8F8F2">>Tab One&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonTitle</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonToolbar</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonHeader</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonContent</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#FF79C6">        {</span><span style="color:#F8F8F2">items.</span><span style="color:#50FA7B">map</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">item</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">i</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">           return</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#8BE9FD;font-style:italic">IonCard</span><span style="color:#50FA7B;font-style:italic"> key</span><span style="color:#FF79C6">={</span><span style="color:#F1FA8C">`</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">i</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">>&#x3C;</span><span style="color:#FF79C6">img</span><span style="color:#50FA7B;font-style:italic"> src</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">item</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">/></span></span>
<span class="line"><span style="color:#F8F8F2">                  &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonCard</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        })</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonContent</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">IonPage</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  );</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><h3>Fetch API</h3>
<p>A list of items also means list of data. In our particular example, we are going to use the <a href="https://dog.ceo/dog-api/" rel="noopener noreferrer external">Dog API</a>, which had become my favorite API for tutorial purpose, to fetch such information. This API is free, open source, don’t need any token and support CORS requests.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> fetchData</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> url</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">https://dog.ceo/api/breeds/image/random/10</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> res</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Response</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> fetch</span><span style="color:#F8F8F2">(url);</span></span>
<span class="line"><span style="color:#F8F8F2">  res</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">json</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">then</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">res</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (res </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> res.message </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> res.message.length </span><span style="color:#FF79C6">></span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#50FA7B">          setItems</span><span style="color:#F8F8F2">([</span><span style="color:#FF79C6">...</span><span style="color:#F8F8F2">items, </span><span style="color:#FF79C6">...</span><span style="color:#F8F8F2">res.message]);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">          setDisableInfiniteScroll</span><span style="color:#F8F8F2">(res.message.length </span><span style="color:#FF79C6">&#x3C;</span><span style="color:#BD93F9"> 10</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">        } </span><span style="color:#FF79C6">else</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">          setDisableInfiniteScroll</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">true</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">      })</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">catch</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">err</span><span style="color:#FF79C6"> =></span><span style="color:#F8F8F2"> console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(err));</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p><em>Note that this example doesn’t contain any pagination, as your real solution would. It just fetch 10 random dogs. Of course for that reason we might have duplicates in our list.</em></p>
<h3>Loading Initial Data</h3>
<p>Ionic provide multiple lifecycle events that we can use in our app. Something I also really appreciated is the fact that they don’t just provide such events for standard components but for functional components too. To fetch data when our page will be displayed, we could hook on the <code>ionViewWillEnter</code> which is fired when the component routing to is about to animate into view.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {useIonViewWillEnter} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@ionic/react</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> Tab1</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> React</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">FC</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">useIonViewWillEnter</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#50FA7B"> fetchData</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><h3>Infinite Scroll</h3>
<p>Let’s cut to the chase, we are now going to add our infinite scroller. Firstly we are adding a new function which will help us fetch new data and stop tell the scroller that the operation has been completed.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> searchNext</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">$event</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> CustomEvent</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">void</span><span style="color:#F8F8F2">>) {</span></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#50FA7B"> fetchData</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    ($event.target </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> HTMLIonInfiniteScrollElement</span><span style="color:#F8F8F2">).</span><span style="color:#50FA7B">complete</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Finally we are importing the Ionic Infinite Scroll components.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { IonInfiniteScroll, IonInfiniteScrollContent } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@ionic/react</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span></code></pre><p>And rendering these in our content as the following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">IonInfiniteScroll</span><span style="color:#50FA7B;font-style:italic"> threshold</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">100px</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    disabled</span><span style="color:#FF79C6">=</span><span style="color:#F1FA8C">{disableInfiniteScroll}</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    onIonInfinite</span><span style="color:#FF79C6">=</span><span style="color:#F1FA8C">{(e:</span><span style="color:#50FA7B;font-style:italic"> CustomEvent</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">&#x3C;void</span><span style="color:#F8F8F2">>) => searchNext(e)}></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">IonInfiniteScrollContent</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">        loadingText</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Loading more good doggos...</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">IonInfiniteScrollContent</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">IonInfiniteScroll</span><span style="color:#F8F8F2">></span></span></code></pre><h3>Altogether</h3>
<p>I split the code in the above steps to makes the implementation more accessible (I hope) but altogether you could find it <a href="https://github.com/peterpeterparker/infiniteScroll/blob/master/src/pages/Tab1.tsx" rel="noopener noreferrer external">GitHub</a> 🐩</p>
<h3>Test</h3>
<p>If everything went according plan, once you run our application with the command line <code>ionic serve</code> , you should be able to browse an infinite list of doggos with your favorite browser 🎉</p>
<p><img src="https://daviddalbusco.com/assets/images/1*zOAnHKLkcqo0M7NMtVioQQ.gif" alt="" / loading="lazy"></p>
<p><em>So much doggos</em></p>
<h3>Filtering</h3>
<p>Displaying a filtered list of items is probably as common as an infinite scroller. Therefore, let’s add a filter, in form of a new state, to our implementation and modify the <code>fetch</code> function to reset the items and query only a specified breed in case a filter would be applied.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> [filter, setFilter] </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> useState</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">>(</span><span style="color:#BD93F9">undefined</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> fetchData</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">reset</span><span style="color:#FF79C6">?:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> dogs</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">[] </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> reset </span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2"> [] </span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> items;</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> url</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> filter </span><span style="color:#FF79C6">?</span></span>
<span class="line"><span style="color:#F1FA8C">        `https://dog.ceo/api/breed/</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">filter</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">/images/random/10`</span><span style="color:#FF79C6"> :</span></span>
<span class="line"><span style="color:#E9F284">        '</span><span style="color:#F1FA8C">https://dog.ceo/api/breeds/image/random/10</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> res</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Response</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> fetch</span><span style="color:#F8F8F2">(url);</span></span>
<span class="line"><span style="color:#F8F8F2">  res</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">json</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">then</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">res</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (res </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> res.message </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> res.message.length </span><span style="color:#FF79C6">></span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#50FA7B">          setItems</span><span style="color:#F8F8F2">([</span><span style="color:#FF79C6">...</span><span style="color:#F8F8F2">dogs, </span><span style="color:#FF79C6">...</span><span style="color:#F8F8F2">res.message]);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">          setDisableInfiniteScroll</span><span style="color:#F8F8F2">(res.message.length </span><span style="color:#FF79C6">&#x3C;</span><span style="color:#BD93F9"> 10</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">        } </span><span style="color:#FF79C6">else</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">          setDisableInfiniteScroll</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">true</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">      })</span></span>
<span class="line"><span style="color:#F8F8F2">      .</span><span style="color:#50FA7B">catch</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">err</span><span style="color:#FF79C6"> =></span><span style="color:#F8F8F2"> console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(err));</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><h3>Fetch And Refresh Data With A React Hook</h3>
<p>To observe the changes which would be applied to the filter and to refresh the data, we are now going to replace the <code>useIonViewWillEnter</code> lifecycle we used before with a <code>useEffect</code> React Hook. Doing so, data are still going to be loaded when we enter our page but moreover, are also going to be fetched on state update.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> React, {useState, useEffect} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> Tab1</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> React</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">FC</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  useEffect</span><span style="color:#F8F8F2">( () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">      fetchData</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">true</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">  }, [filter]);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>We import the Ionic buttons and labels.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { IonButton, IonLabel } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@ionic/react</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span></code></pre><p>And are finally using these components to trigger the filtering of the list of<br />
dogs with only dachshund.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">IonButton</span><span style="color:#50FA7B;font-style:italic"> onClick</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">{()</span><span style="color:#E9F284">"</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">=</span><span style="color:#E9F284">""</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    setFilter('dachshund')}></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">IonLabel</span><span style="color:#F8F8F2">>Filter&#x3C;/</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">IonLabel</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">IonButton</span><span style="color:#F8F8F2">></span></span></code></pre><h3>Altogether</h3>
<p>I have implemented this filter example in the second tab of the demo solution I’ve uploaded to <a href="https://github.com/peterpeterparker/infiniteScroll/blob/master/src/pages/Tab2.tsx" rel="noopener noreferrer external">GitHub</a> 🐕</p>
<h3>Test</h3>
<p>If you run the application again, you should now be able to filter the list with only dachshund 🥳</p>
<p><img src="https://daviddalbusco.com/assets/images/1*dCTUxVxI8yfkxGsqtV0baA.gif" alt="" / loading="lazy"></p>
<p><em>So much dachshund</em></p>
<h3>Cherry on the cake 🍒🎂</h3>
<p>Infinite Scroll is only one of the wonderful features of Ionic + React and only one of those I have used in the prototype I have implemented for our Meetup. Furthermore, the small application I’ve built is free and open source, give it a try, give a try to <a href="https://wooof.ch" rel="noopener noreferrer external">Wooof</a> 🐶</p>
<p>To infinity and beyond 🚀</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[How to keep secret your Font Awesome Pro token in public GitHub actions]]></title>
          <description><![CDATA[Configure your GitHub actions to keep secret your Font Awesome Pro token or NPM private tokens]]></description>
          <link>https://daviddalbusco.com/blog/how-to-keep-secret-your-font-awesome-pro-token-in-public-github-actions/</link>
          <pubDate>Wed, 27 Nov 2019 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*oASKWYL2zJdXrSghTn92PA.jpeg" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@serkanturk?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Serkan Turk</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>Yesterday <a href="https://twitter.com/julienrenaux" rel="noopener noreferrer external">Julien Renaux</a> published a <a href="https://twitter.com/julienrenaux/status/1199322520815583233" rel="noopener noreferrer external">series</a> of interesting new blog posts. In one of these he notably displayed, step by step, <a href="https://julienrenaux.fr/2019/11/25/building-deploying-stenciljs-apps-firebase-hosting-github-actions/" rel="noopener noreferrer external">how to deploy StencilJS apps with GitHub actions and Firebase hosting</a>.</p>
<p>This tutorial really caught my eye because all applications of our web open source project <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a> are developed with <a href="https://stenciljs.com" rel="noopener noreferrer external">StencilJS</a> and hosted on <a href="https://firebase.google.com" rel="noopener noreferrer external">Firebase</a> 😁</p>
<p>Of course I couldn’t wait that much and I had to give it a try. Because I’ll have to make changes to obfuscate production tokens differently if I use a CI to build our editor for presentations, I thought I could first try out his recipe in the current <a href="https://ionicframework.com/docs/react" rel="noopener noreferrer external">Ionic React</a> demo application I’m developing for our <a href="https://www.meetup.com/fr-FR/Ionic-Zurich/events/265767496/" rel="noopener noreferrer external">Meetup</a> next week.</p>
<p>As a result, it just worked seamlessly 🎉 It worked so well and the setup was so easy, that I thought I could quickly setup another CI deployment for my <a href="https://daviddalbusco.com" rel="noopener noreferrer external">website</a> too.</p>
<p>Unfortunately it didn’t worked out 😕 Even if the technology and stack of my website, build with <a href="https://www.gatsbyjs.org" rel="noopener noreferrer external">Gatsby</a>, is somehow really similar to the small app, the build performed by the CI failed:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F1FA8C">X Download Artifact</span></span>
<span class="line"><span style="color:#F1FA8C">  1 Run actions/download-artifact@master</span></span>
<span class="line"><span style="color:#8BE9FD">  2 with</span><span style="color:#FF79C6">:</span></span>
<span class="line"><span style="color:#8BE9FD">  3    name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> build</span></span>
<span class="line"><span style="color:#8BE9FD">  4 Download artifact 'build' to</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">/home/runner/work/daviddalbusco.com/daviddalbusco.com/build</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#BD93F9">  5</span></span>
<span class="line"><span style="color:#BD93F9">6</span></span></code></pre><p>At first, I didn’t understood the error. Of course I noticed that some artifacts couldn’t be downloaded but I didn’t knew what where these and what was the root cause. After a bit I finally remembered that I used to face a similar problem with another CI.</p>
<p>For my website I’m using <a href="https://fontawesome.com" rel="noopener noreferrer external">Font Awesome</a> and notably the <code>@fortawesome/pro-light-svg-icons</code> icons which could only be used if my related Font Awesome Pro NPM token is configured. While the documentation explains clearly how to configure the token locally, they don’t (or at least I didn’t found out at first) provide an example which displays how to hide it from a public repository. Therefore, the purpose of this blog post.</p>
<h3>Configure the NPM registry</h3>
<p>The Font Awesome NPM token could either be set up globally or per project. The method we need to build our project with a CI is the last one, “per project”. For that purpose, we create a new file <code>.npmrc</code> in the root of your project . To the contrary of the documentation, we only add one line in the file respectively we only add the reference to the Font Awesome packet manager. We deliberately ignore to specify anything regarding our token.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B;font-style:italic">fortawesome</span><span style="color:#F8F8F2">:</span><span style="color:#50FA7B;font-style:italic">registry</span><span style="color:#FF79C6">=</span><span style="color:#50FA7B;font-style:italic">https</span><span style="color:#F8F8F2">:</span><span style="color:#6272A4">//npm.fontawesome.com/</span></span></code></pre><h3>Set up a new secret in GitHub</h3>
<p>As we didn’t provided the token in the previous file, we will have to provide it to the GitHub action in a different way otherwise it will still not be able to download the dependency. Fortunately GitHub already provide an option to keep environment variables secret. Therefore we make the most of this option and we create a new secret for our Font Awesome NPM token. For example let’s call it <code>FONTAWESOME_NPM_AUTH_TOKEN</code>.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*NVoURzVa2jn6uuvAKvo12Q.png" alt="" / loading="lazy"></p>
<p><em>Set up a GitHub secret for the Font Awesome NPM token</em></p>
<h3>Use the secret in our CI</h3>
<p>If you already followed the tutorial of Julien, your CI workflow probably already contains the following to install the dependencies from NPM of your application:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">-</span><span style="color:#8BE9FD"> name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> Install Dependencies</span></span>
<span class="line"><span style="color:#8BE9FD">        run</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> npm ci</span></span></code></pre><p>This is the root of our problem, as NPM is not aware of the specific registry and token it has to use to download the specific artifact. Therefore we finally are going to resolve the issue by running a command-line, before the installation one, to configure NPM with our secret token.</p>
<p>The pipe (<code>|</code> ) let us specify multiple commands all run in the in the same shell (see <a href="https://help.github.com/en/actions/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions#jobsjob_idstepsrun" rel="noopener noreferrer external">documentation</a>).</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">-</span><span style="color:#8BE9FD"> name</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> Install Dependencies</span></span>
<span class="line"><span style="color:#8BE9FD">      run</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> |</span></span>
<span class="line"><span style="color:#F1FA8C">        npm config set '//npm.fontawesome.com/:_authToken' "${{ secrets.FONTAWESOME_NPM_AUTH_TOKEN }}"</span></span>
<span class="line"><span style="color:#F1FA8C">        npm ci</span></span></code></pre><p>And voilà, our CI should now be able to download the required dependency and therefore build and deploy our project 🎉</p>
<p><img src="https://daviddalbusco.com/assets/images/1*B0QAOSrrjgtLV5zywnA8sg.png" alt="" / loading="lazy"></p>
<p><em>Successful CI, happy CI 😺</em></p>
<h3>Cherry on the cake 🍒🎂</h3>
<p>My website is open source and available on <a href="https://github.com/peterpeterparker/daviddalbusco.com" rel="noopener noreferrer external">GitHub</a>, check it out to validate the solution with your own eyes 😉</p>
<p>To infinity and beyond 🚀</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Introducing the new remote control for your presentations]]></title>
          <description><![CDATA[Introducing the new remote control for your presentations developed with the web open source editor DeckDeckGo]]></description>
          <link>https://daviddalbusco.com/blog/introducing-the-new-remote-control-for-your-presentations/</link>
          <pubDate>Tue, 12 Nov 2019 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*pk4BfD4Sqt6gHIX4lpYO8g.png" alt="" / loading="lazy"></p>
<blockquote>
<p>Change is the essential process of all existence - Spock</p>
</blockquote>
<p>A little more than a month ago, we <a href="https://daviddalbusco.com/blog/introducing-deckdeckgo-the-web-open-source-editor-for-presentations" rel="noopener noreferrer external">launched</a> our web open source editor for presentations: <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a></p>
<p>It went way above our expectations, quickly three thousand people had tried out our editor and we received a lot of positive feedback. It was really an unexpected start and just an heartwarming experience. Thank you all, for everything 🙏</p>
<p>Following this release we spent the month of October improving our platform, thanks to our community and many Pull Requests. It was definitely a really nice <a href="https://hacktoberfest.digitalocean.com" rel="noopener noreferrer external">Hacktoberfest 2019</a> season for us.</p>
<p>Finally came the time to ask ourselves: <strong>And now, what’s next?</strong></p>
<p>We quickly identified two new cool ideas, which would be fun to implement, unique and useful for our users. But you know what? These aren’t the ones we are publishing today 🤣 We thought that before chasing these, we should first resolve one of the most users’ requested feature (“speaker notes”) and improve a long time known weakness, at least in my eyes, the UX of our controller.</p>
<p>That’s why we are happy to launch today our brand new <a href="https://deckdeckgo.app" rel="noopener noreferrer external">remote control</a> 🎉</p>
<iframe width="280" height="158" src="https://www.youtube.com/embed/PnSNT5WpauE" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
<p><em>A demo of our brand new remote control</em></p>
<h3>So, what’s new?</h3>
<p>Enough teasing, here are the major new features of our remote controller.</p>
<h3>Speaker notes with Markdown support</h3>
<p>Through our <a href="https://join.slack.com/t/deckdeckgo/shared_invite/enQtNzM0NjMwOTc3NTI0LTBlNmFhODNhYmRkMWUxZmU4ZTQ2MDJiNjlmYWZiODNjMDU5OGRjYThlZmZjMTc5YmQ3MzUzMDlhMzk0ZDgzMDY" rel="noopener noreferrer external">Slack</a> channel, <a href="https://github.com/deckgo/deckdeckgo/projects/4" rel="noopener noreferrer external">GitHub</a> or messages, being able to add “<strong>speaker notes</strong> for the slides in the web editor” was often the most requested feature. Fortunately, it was already implemented in our core and even available in the developer kit, therefore, the biggest challenge was “only” its integration in terms of UX. Moreover, when implementing the feature, we thought that it would be handy if <strong>Markdown</strong> would be supported too 😃</p>
<p><img src="https://daviddalbusco.com/assets/images/1*uKHylyasNOXQu3W3Uc7uhw.png" alt="" / loading="lazy"></p>
<p><em>Edit your speaker notes with Markdown in our web open source editor</em></p>
<h3>Synchronized content</h3>
<p>Ever since I implemented the very first version of our remote control, we always thought that it would be just really cool to synchronize the content between presentation and controller. Guess what, we did it 😉</p>
<p>It’s worth to notice that the presentation don’t have to be published on internet in order to be synchronized. As for the connection and commands, it is performed through WebRTC and therefore work even if you run your presentation locally 😁</p>
<p><em>Note: this new solution is a <strong>breaking change</strong>. It is automatically rolled out in our web editor but if you have already published a deck, you just would have to publish it again in order to upgrade it.</em></p>
<p><img src="https://daviddalbusco.com/assets/images/1*NfYAdoMvNHAuaMOf809-iA.png" alt="" / loading="lazy"></p>
<p><em>Synchronized content</em></p>
<h3>New user experience</h3>
<p>Although all functions (swiping the slides, drawing over the presentations, playing a video, displaying the notes, etc.) were implemented in the remote control, I never felt fully convinced by its UX. I was kind of ok with but had for months somewhere in my mind the idea that I needed to improve its behavior. The actions to trigger the swipe of the slides and to play video were too small, specially to be used during a talk, IRL. That’s why these were the main concern of the redesign and are now filling half of the screen size of the remote control 😊</p>
<p>Moreover, the speaker notes weren’t really cleverly displayed. That’s why we moved them to a new sheet component which could be opened with a “swipe up” gesture.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*pQx-i5jfIdweWYTIu_9KzQ.gif" alt="" / loading="lazy"></p>
<p><em>New user experience</em></p>
<h3>Responsive design for tablets</h3>
<p>Earlier October I attended the <a href="https://dinacon.ch" rel="noopener noreferrer external">DINAcon</a> 2019 conference in Bern. The speakers were presenting their subjects on a shiny stage, behind a stylish thin desk which aimed, I guess, to be discrete but on the other hand, they were still using a laptop to have a look at their talk and notes 🤔</p>
<p><img src="https://daviddalbusco.com/assets/images/1*iXSRsFNZUOItDFyevXYsqQ.png" alt="" / loading="lazy"></p>
<p><em><a href="https://twitter.com/katsi111" rel="noopener noreferrer external">Katariina Kari</a> speaking about “Knowledge Graph” and having a quick look at her laptop at DINACon 2019 (source <a href="https://www.flickr.com/photos/140845441@N04/48947571397/in/album-72157711466719708" rel="noopener noreferrer external">Flickr</a>)</em></p>
<p>Suddenly it hit me: why not having a discrete <strong>dashboard</strong> displayed on a <strong>tablet</strong> instead of a laptop? I started to think about it and imagined that delivering a talk could be like being on the USS Enterprise bridge with all its commands to make it fly 🤣</p>
<p><em>If you read these lines I’m guessing that now you know why the cover photo is a bit inspired by Star Trek.</em></p>
<p>Moreover of the user experience, this new version of our remote control introduces a new responsive design which aims to implement this vision and aims to deliver all the information and commands needed to perform your talk with tablet devices or any browsers in landscape mode as support.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*Nhax5Zz-ReP2Qq0E3mOjqQ.gif" alt="" / loading="lazy"></p>
<p><em>Inspired from the USS Enterprise bridge</em></p>
<h3>Hello darkness my old friend</h3>
<p>Last goodies, as we already did with our <a href="https://docs.deckdeckgo.com" rel="noopener noreferrer external">documentation</a> for developers, we also rolled out a light and dark theme switcher 🌓</p>
<p><img src="https://daviddalbusco.com/assets/images/1*k_xss5uP7Jeb5zAK7hAwmQ.gif" alt="" / loading="lazy"></p>
<p><em>Switch between light and dark theme</em></p>
<h3>Get Started Now</h3>
<p>A picture speaks a thousand words — give it a try 😉</p>
<ol>
<li>Open the <a href="https://deckdeckgo.app" rel="noopener noreferrer external">remote control</a> on your phone or table</li>
<li>Start a <a href="https://beta.deckdeckgo.io/daviddalbusco/introducing-the-new-deckdeckgo-remotecontrol/" rel="noopener noreferrer external">presentation</a> on another device</li>
<li>Have fun 🎉</li>
</ol>
<p>To infinity and beyond 🚀</p>
<p>David</p>
<p>P.S.: Background photo of the cover by <a href="https://unsplash.com/@shotbycerqueira?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Cerqueira</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Trick JavaServer Faces, load your bean data from the client side]]></title>
          <description><![CDATA[How to trick JSF (JavaServer Faces) while loading your bean data from the client side with the help of Primefaces]]></description>
          <link>https://daviddalbusco.com/blog/trick-javaserver-faces-load-your-bean-data-from-the-client-side/</link>
          <pubDate>Mon, 04 Nov 2019 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*QSKH-4KVY9VB1ssOElKYhQ.jpeg" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@shawnpangg?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Shawn Pang</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>Commonly we are using JavaServer Faces (JSF) to access to server-side data and logic but it might happen, that we would actually have to fetch data on the client side and would then have to inject these in our server managed beans.</p>
<p>For example, let’s imagine that your client absolutely want to display a random cute picture provided by the third party <a href="https://dog.ceo/dog-api/" rel="noopener noreferrer external">Dog API</a>, which we are going to use in this article, in the application but that the server, where the Java application is running, have definitely no internet access despite trying to convince both company and server administrator, that it can’t be developed without internet access.</p>
<p>In such a special case and in a classical Swiss🇨🇭 way of resolving issue, you will probably then have to find a consensus and to implement a trick (or hack, depends if you see the glass half full or half empty 😉) as the one we are going to develop in this blog post.</p>
<h3>Before We Start</h3>
<p>To follow this solution you will need a Java project where both JSF and <a href="https://www.primefaces.org" rel="noopener noreferrer external">Primefaces</a> are implemented. If you have none or if you would like to create a blank one, you could proceed as I displayed in my previous article “<a href="https://medium.com/swlh/create-a-primefaces-jsf-project-with-maven-and-wildfly-bb695bed84c8" rel="noopener noreferrer external">Create a Primefaces JSF project with Maven and Widfly</a>”.</p>
<h3>Getting Started</h3>
<p>The first time I faced such a dead end as pictured above, I didn’t knew where to begin. After a bit of research I finally found out the cornerstone of the solution respectively the <a href="https://www.primefaces.org/showcase/ui/ajax/remoteCommand.xhtml" rel="noopener noreferrer external">Primefaces</a> <code>&lt;p:remotecommand/&gt;</code> which provides a simple way to execute backing bean methods with Javascript. Using it, we are able to send data from Javascript to the beans, decode these information and ultimately “convert” them in bean’s values.</p>
<h4>Workflow</h4>
<p>The workflow of the solution and the steps we are going to follow are the following:</p>
<ol>
<li>Fetch data from the 3rd party API using Javascript</li>
<li>Pass the results from Javascript to the Java bean using <code>&lt;p:remotecommand/&gt;</code></li>
<li>Parse the information to actual bean object values</li>
<li>Display the bean values on the client side</li>
<li>Init the page with a data</li>
</ol>
<p>Let’s get started 🚀</p>
<h3>1. Fetch data from the 3rd party API using Javascript</h3>
<p>To begin our implementation we are firstly creating a new page <code>src/main/webapp/dogs.xhtml</code> which contains a button, to trigger manually the start of our process, and the remote command. Both components have to be contained in a form.</p>
<p>Moreover we also add the actual implementation of the 3rd party API data fetching. For that purpose, we use the Javascript <a href="https://developer.mozilla.org/fr/docs/Web/API/Fetch_API/Using_Fetch" rel="noopener noreferrer external">Fetch API</a>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;!</span><span style="color:#FF79C6">DOCTYPE</span><span style="color:#50FA7B;font-style:italic"> html</span><span style="color:#50FA7B;font-style:italic"> PUBLIC</span><span style="color:#F1FA8C"> "-//W3C//DTD XHTML 1.0 Transitional//EN"</span><span style="color:#F1FA8C"> "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">html</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    xmlns</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">http://www.w3.org/1999/xhtml</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    xmlns:h</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">http://java.sun.com/jsf/html</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    xmlns:p</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">http://primefaces.org/ui</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">h:head</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">title</span><span style="color:#F8F8F2">>Random dog&#x3C;/</span><span style="color:#FF79C6">title</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">h:head</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">h:body</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#50FA7B;font-style:italic"> type</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">text/javascript</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#FF79C6">            const</span><span style="color:#50FA7B"> randomDog</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">                const</span><span style="color:#F8F8F2"> rawResponse </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> fetch</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://dog.ceo/api/breeds/image/random</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">                if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">rawResponse </span><span style="color:#FF79C6">||</span><span style="color:#FF79C6"> !</span><span style="color:#F8F8F2">rawResponse.ok) {</span></span>
<span class="line"><span style="color:#F8F8F2">                    console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(rawResponse);</span></span>
<span class="line"><span style="color:#FF79C6">                    return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">                }</span></span>
<span class="line"><span style="color:#FF79C6">                const</span><span style="color:#F8F8F2"> result </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> rawResponse.</span><span style="color:#50FA7B">text</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">            };</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">h:form</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">p:remoteCommand</span><span style="color:#50FA7B;font-style:italic"> name</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">loadResult</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">p:commandButton</span><span style="color:#50FA7B;font-style:italic"> value</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Load a random dog</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> onclick</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">randomDog();</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">f:ajax</span><span style="color:#50FA7B;font-style:italic"> execute</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">@form</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> render</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">@none</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">p:commandButton</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">h:form</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">h:body</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">html</span><span style="color:#F8F8F2">></span></span></code></pre><h3>2. Pass the results to the bean</h3>
<p>You may have noticed that the above <code>&lt;p:button/&gt;</code> doesn’t classically call a bean action or listener but rather call immediately the Javascript function <code>randomDog();</code> . It means that first of all, we are fetching the data from the client side. Therefore we should now to pass the results to the bean. For that purpose we create a new bean <code>src/main/java/DogsBean.java</code> which exposes a method <code>load()</code> in order to, guess what, load later one our data 😁.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> java.io.Serializable;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> javax.faces.view.ViewScoped;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> javax.inject.Named;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#8BE9FD;font-style:italic">Named</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">dogs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#8BE9FD;font-style:italic">ViewScoped</span></span>
<span class="line"><span style="color:#FF79C6">public</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> DogsBean</span><span style="color:#FF79C6"> implements</span><span style="color:#8BE9FD;font-style:italic"> Serializable</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">   public</span><span style="color:#8BE9FD;font-style:italic"> void</span><span style="color:#50FA7B"> load</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#6272A4">      // TODO load the data to object values</span></span>
<span class="line"><span style="color:#F8F8F2">   }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Our bean being ready, we could now improve our servlet respectively we link the <code>&lt;p:remotecommand/&gt;</code> with the bean <code>load()</code> method.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">h:form</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">p:remoteCommand</span><span style="color:#50FA7B;font-style:italic"> name</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">loadResult</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> action</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">#{dogs.load()}</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> process</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">@this</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> update</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">@form</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">h:form</span><span style="color:#F8F8F2">></span></span></code></pre><p>We complete the chain by calling the remote command with the result of the Javascript <code>fetch</code> 🤓 . For that purpose, we use the function created by the Primefaces remote command, identified with the name <code>loadResult</code> we provided, and we pass the information as a new JSON array containing an identifier, a <code>name</code> , and a <code>value</code> , the result of the fetch respectively the data as text.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#50FA7B;font-style:italic"> type</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">text/javascript</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#6272A4">      // same code as above</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">      const</span><span style="color:#F8F8F2"> result </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> rawResponse.</span><span style="color:#50FA7B">text</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">      loadResult</span><span style="color:#F8F8F2">([{</span></span>
<span class="line"><span style="color:#F8F8F2">        name</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">dog</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        value</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> result</span></span>
<span class="line"><span style="color:#F8F8F2">      }]);</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span></code></pre><p>This call will submit a new request to the server. We could therefore access the parameters of the request in our bean to find out the data we are interested in. These are identified with the identifier, the <code>name</code> we provided above respectively <code>'dog'</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">public</span><span style="color:#8BE9FD;font-style:italic"> void</span><span style="color:#50FA7B"> load</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">   final</span><span style="color:#8BE9FD;font-style:italic"> String</span><span style="color:#F8F8F2"> jsonData </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> FacesContext.</span><span style="color:#50FA7B">getCurrentInstance</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">                           .</span><span style="color:#50FA7B">getExternalContext</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">                           .</span><span style="color:#50FA7B">getRequestParameterMap</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">                           .</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">dog</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><h3>3. Parse the information to bean object values</h3>
<p>The data delivered by the Dog API are provided as JSON data, for example:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">message</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">: </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://images.dog.ceo/breeds/coonhound/n02089078_2794.jpg</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">status</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">: </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">success</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Therefore, in order to parse these to Java object values, we create a new corresponding data transfer object (DTO).</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> java.io.Serializable;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">public</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> DogDTO</span><span style="color:#FF79C6"> implements</span><span style="color:#8BE9FD;font-style:italic"> Serializable</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">   private</span><span style="color:#8BE9FD;font-style:italic"> String</span><span style="color:#F8F8F2"> message;</span></span>
<span class="line"><span style="color:#FF79C6">   private</span><span style="color:#8BE9FD;font-style:italic"> String</span><span style="color:#F8F8F2"> status;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">   public</span><span style="color:#8BE9FD;font-style:italic"> String</span><span style="color:#50FA7B"> getMessage</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">      return</span><span style="color:#F8F8F2"> message;</span></span>
<span class="line"><span style="color:#F8F8F2">   }</span></span>
<span class="line"><span style="color:#FF79C6">   public</span><span style="color:#8BE9FD;font-style:italic"> void</span><span style="color:#50FA7B"> setMessage</span><span style="color:#F8F8F2">(</span><span style="color:#8BE9FD;font-style:italic">String</span><span style="color:#FFB86C;font-style:italic"> message</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">      this</span><span style="color:#F8F8F2">.message </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> message;</span></span>
<span class="line"><span style="color:#F8F8F2">   }</span></span>
<span class="line"><span style="color:#FF79C6">   public</span><span style="color:#8BE9FD;font-style:italic"> String</span><span style="color:#50FA7B"> getStatus</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">      return</span><span style="color:#F8F8F2"> status;</span></span>
<span class="line"><span style="color:#F8F8F2">   }</span></span>
<span class="line"><span style="color:#FF79C6">   public</span><span style="color:#8BE9FD;font-style:italic"> void</span><span style="color:#50FA7B"> setStatus</span><span style="color:#F8F8F2">(</span><span style="color:#8BE9FD;font-style:italic">String</span><span style="color:#FFB86C;font-style:italic"> status</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">      this</span><span style="color:#F8F8F2">.status </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> status;</span></span>
<span class="line"><span style="color:#F8F8F2">   }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Having both data and Java object, we could now deserialize the information using the Google <a href="https://github.com/google/gson" rel="noopener noreferrer external">gson</a> library which we add as a new dependency in our <code>pom.xml</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">dependency</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;</span><span style="color:#FF79C6">groupId</span><span style="color:#F8F8F2">>com.google.code.gson&#x3C;/</span><span style="color:#FF79C6">groupId</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;</span><span style="color:#FF79C6">artifactId</span><span style="color:#F8F8F2">>gson&#x3C;/</span><span style="color:#FF79C6">artifactId</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;</span><span style="color:#FF79C6">version</span><span style="color:#F8F8F2">>2.8.6&#x3C;/</span><span style="color:#FF79C6">version</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">dependency</span><span style="color:#F8F8F2">></span></span></code></pre><p>We declare declare the above DTO as a member of the bean class and we effectively process the parsing of the data in our <code>load()</code> method.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> java.io.Serializable;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> javax.faces.context.FacesContext;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> javax.faces.view.ViewScoped;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> javax.inject.Named;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> com.google.gson.Gson;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> com.google.gson.GsonBuilder;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#8BE9FD;font-style:italic">Named</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">dogs</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#8BE9FD;font-style:italic">ViewScoped</span></span>
<span class="line"><span style="color:#FF79C6">public</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> DogsBean</span><span style="color:#FF79C6"> implements</span><span style="color:#8BE9FD;font-style:italic"> Serializable</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">   private</span><span style="color:#8BE9FD;font-style:italic"> DogDTO</span><span style="color:#F8F8F2"> dog;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">   public</span><span style="color:#8BE9FD;font-style:italic"> void</span><span style="color:#50FA7B"> load</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">      final</span><span style="color:#8BE9FD;font-style:italic"> String</span><span style="color:#F8F8F2"> jsonData </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> FacesContext.</span><span style="color:#50FA7B">getCurrentInstance</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">                           .</span><span style="color:#50FA7B">getExternalContext</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">                           .</span><span style="color:#50FA7B">getRequestParameterMap</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">                           .</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">dog</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">      final</span><span style="color:#8BE9FD;font-style:italic"> Gson</span><span style="color:#F8F8F2"> gson </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> GsonBuilder</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">create</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">      dog </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> gson.</span><span style="color:#50FA7B">fromJson</span><span style="color:#F8F8F2">(jsonData, DogDTO.class);</span></span>
<span class="line"><span style="color:#F8F8F2">   }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">   public</span><span style="color:#8BE9FD;font-style:italic"> DogDTO</span><span style="color:#50FA7B"> getDog</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">      return</span><span style="color:#F8F8F2"> dog;</span></span>
<span class="line"><span style="color:#F8F8F2">   }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><h3>4. Display the bean values on the client side</h3>
<p>We have now fetched data on the client side, sent these to the server but the user still don’t notice any results. For that reason we add a new image to our page which uses our DTO <code>message</code> variable as source.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">h:form</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">p:outputPanel</span><span style="color:#50FA7B;font-style:italic"> layout</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">block</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> rendered</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">#{dogs.dog != null}</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">img</span><span style="color:#50FA7B;font-style:italic"> src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">#{dogs.dog.message}</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> alt</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">A random dog</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">p:outputPanel</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">h:form</span><span style="color:#F8F8F2">></span></span></code></pre><p>That’s it, our implementation is ready. We could start our application server in a terminal to try out the solution.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">mvn</span><span style="color:#F1FA8C"> clean</span><span style="color:#F1FA8C"> install</span><span style="color:#F8F8F2"> &#x26;&#x26; </span><span style="color:#50FA7B">mvn</span><span style="color:#F1FA8C"> wildfly:run</span></span></code></pre><p>If everything goes according plan, we could open our application in our favorite browser at the address <a href="http://localhost:8080/jsf-dogs/dogs.xhtml" rel="noopener noreferrer external">http://localhost:8080/jsf-dogs/dogs.xhtml</a> and should now be able to fetch a random dog each time we call our action 😊</p>
<p><img src="https://daviddalbusco.com/assets/images/1*lolMm4r-XdSiapWohK4pLg.gif" alt="" / loading="lazy"></p>
<p><em>So much doggy 😍</em></p>
<h3>5. Init the page with a data</h3>
<p>This step isn’t mandatory but I think it’s interesting to notice that it is also possible to load data from the client side when the page is accessed. Basically, from the sever side, in our bean, we execute a Javascript function on the client side, once everything is loaded from the lifecycle <code>PostContruct</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">@</span><span style="color:#8BE9FD;font-style:italic">PostConstruct</span></span>
<span class="line"><span style="color:#FF79C6">public</span><span style="color:#8BE9FD;font-style:italic"> void</span><span style="color:#50FA7B"> init</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#F8F8F2">   PrimeFaces.</span><span style="color:#50FA7B">current</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">executeScript</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">randomDog();</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>That’s it, we dit it! We could restart our server and test our final implementation 🎉</p>
<p><img src="https://daviddalbusco.com/assets/images/1*jjw6lTngEY7xlODjsbKYjQ.gif" alt="" / loading="lazy"></p>
<p><em>An initial doggy and so much other doggy 😍</em></p>
<h3>Cherry on the cake 🍒🎂</h3>
<p>If you want to avoid the hassle of creating your own project and copying/pasting the above code, I have published the source code and project online on <a href="https://github.com/peterpeterparker/jsf-dogs" rel="noopener noreferrer external">GitHub</a>, be my guest and as always, I would be really happy to hear your feedback 😃</p>
<p>To infinity and beyond 🚀</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Highlight Markdown code with Remarkable and a Web Component]]></title>
          <description><![CDATA[Highlight Markdown code with Remarkable and a Web Component]]></description>
          <link>https://daviddalbusco.com/blog/highlight-markdown-code-with-remarkable-and-a-web-component/</link>
          <pubDate>Sat, 02 Nov 2019 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*TfrSMW1qpZb_6x0opEVNwQ.jpeg" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@tdpatch?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Tim Patch</a> on <a href="https://unsplash.com/s/photos/highlight?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>We are currently developing many improvements for the upcoming new release of the remote control of <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>, our open source web editor for presentations. One of these will give our users the ability to write notes in our editor and to display them in the controller more conveniently. But more than that, it will also be possible to write these using Markdown 😃</p>
<p>Earlier today, <a href="https://twitter.com/fsfarimani" rel="noopener noreferrer external">Foad Sojoodi Farimani</a> shared with us the excellent idea of going a step further with the goal to highlight the code blocks provided as Markdown notes.</p>
<p>I found the idea absolutely awesome and I almost began directly to implement it (almost because I had to clean my flat first, weekend duties are the best 🤣). While commonly you would provide a library to the markdown parser you are using, in our case <a href="https://github.com/jonschlinkert/remarkable" rel="noopener noreferrer external">Remarkable</a> from <a href="https://twitter.com/jonschlinkert" rel="noopener noreferrer external">Jon Schlinkert</a>, to highlight the code, I solved it instead by actually parsing it with the help of our Web Component <code>&lt;deckgo-highlight-code/&gt;</code> 🤪.</p>
<p>This <a href="https://docs.deckdeckgo.com/components/code" rel="noopener noreferrer external">component</a> is a wrapper which under the hood use <a href="https://prismjs.com" rel="noopener noreferrer external">PrismJS</a>, from <a href="https://lea.verou.me" rel="noopener noreferrer external">Lea Verou</a> and <a href="https://twitter.com/jamesdigioia" rel="noopener noreferrer external">James DiGioia</a>. It has the advantage to fetch at runtime the definition of the language it has to interpret which is kind of handy if the provided code could be from any types.</p>
<p>As it took me a bit of time to figure out how to bring everything together, I thought about letting a trace of it through this new blog post.</p>
<h3>Installation</h3>
<p>In your project, install both the markdown parser and the Web Component:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> install</span><span style="color:#F1FA8C"> remarkable</span><span style="color:#F1FA8C"> @deckdeckgo/highlight-code</span><span style="color:#BD93F9"> --save</span></span></code></pre><h3>Importation</h3>
<p>One installed, import the library and the component in your application:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Remarkable } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">remarkable</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@deckdeckgo/highlight-code</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span></code></pre><h3>Solution</h3>
<p>Instead of providing a highlighter function when instantiating a new Remarkable object, as displayed in the documentation, we are going to define our own custom rule to parse the code respectively we don’t provide any highlighter.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> md</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Remarkable</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Remarkable</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    html</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    xhtmlOut</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    breaks</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><p>Our goal is to replace the automatically generated HTML code <code>&lt;pre&gt;&lt;code/&gt;&lt;pre&gt;</code> with our custom Web Component <code>&lt;deckgo-highlight-code/&gt;</code>. Therefore we create our own rule in a custom function. Moreover, as the code could be either inlined or provided as block, we add a variable to the function in order to handle both cases respectively both styles.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> codeRule</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">inline</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span></span>
<span class="line"><span style="color:#F8F8F2">  (</span><span style="color:#FFB86C;font-style:italic">tokens</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">idx</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">_options</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">_env</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F1FA8C"> `&#x3C;deckgo-highlight-code</span></span>
<span class="line"><span style="color:#FF79C6">            ${</span><span style="color:#F8F8F2">inline</span><span style="color:#FF79C6"> ?</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">class="inline"</span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> :</span><span style="color:#E9F284"> ''</span><span style="color:#FF79C6">}</span></span>
<span class="line"><span style="color:#F1FA8C">            language="</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">tokens[idx].params</span><span style="color:#FF79C6"> ?</span></span>
<span class="line"><span style="color:#F8F8F2">                tokens[idx].params</span><span style="color:#FF79C6"> :</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">javascript</span><span style="color:#E9F284">'</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">"></span></span>
<span class="line"><span style="color:#F1FA8C">                    &#x3C;code slot="code"></span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">tokens[idx].content</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">&#x3C;/code></span></span>
<span class="line"><span style="color:#F1FA8C">        &#x3C;/deckgo-highlight-code>`</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Our custom rule being defined, we set it to Remarkable in order to transform code (“inline code”) and fence (“block of code”).</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">md.renderer.rules.code </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> codeRule</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">true</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">md.renderer.rules.fence </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> codeRule</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">false</span><span style="color:#F8F8F2">);</span></span></code></pre><p>To parse the Markdown content to HTML, we finally proceed as we would do normally using the <code>render</code> method.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">md.</span><span style="color:#50FA7B">render</span><span style="color:#F8F8F2">(content);</span></span></code></pre><h3>Style</h3>
<p>To handle both styles, inline and block, we provide a specific display to the inlined one.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#BD93F9">deckgo-highlight-code</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">{</span></span>
<span class="line"><span style="color:#FF79C6">  &#x26;</span><span style="color:#50FA7B;font-style:italic">.inline</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">{</span></span>
<span class="line"><span style="color:#8BE9FD">    --deckgo-highlight-code-container-display</span><span style="color:#F8F8F2">: </span><span style="color:#BD93F9">inline-flex</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">;</span></span>
<span class="line"><span>  </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">}</span></span>
<span class="line"><span style="color:#FF5555;font-style:italic;text-decoration:underline">}</span></span></code></pre><h3>Test</h3>
<p>We can finally try out our solution. For that purpose, we try to render a simple piece of code to the console.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> content</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#F1FA8C"> `# Title</span></span>
<span class="line"><span style="color:#F1FA8C">A line </span><span style="color:#FF79C6">\`</span><span style="color:#F1FA8C">console.log('Inline code');</span><span style="color:#FF79C6">\`</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">\`\`\`</span></span>
<span class="line"><span style="color:#F1FA8C">console.log('Block code');</span></span>
<span class="line"><span style="color:#FF79C6">\`\`\`</span></span>
<span class="line"><span style="color:#F1FA8C">                `</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(md.</span><span style="color:#50FA7B">render</span><span style="color:#F8F8F2">(content));</span></span></code></pre><p>If everything goes according plan, the output should be the following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">>Title&#x3C;/</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    A line</span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">deckgo-highlight-code</span><span style="color:#50FA7B;font-style:italic"> class</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">inline</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> language</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">javascript</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">code</span><span style="color:#50FA7B;font-style:italic"> slot</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">code</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>console.log('Inline code'); &#x3C;/</span><span style="color:#FF79C6">code</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">deckgo-highlight-code</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">p</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">deckgo-highlight-code</span><span style="color:#50FA7B;font-style:italic"> language</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">javascript</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">code</span><span style="color:#50FA7B;font-style:italic"> slot</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">code</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>console.log('Block code'); &#x3C;/</span><span style="color:#FF79C6">code</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">deckgo-highlight-code</span><span style="color:#F8F8F2">></span></span></code></pre><p>Voilà, that’s it, we did it, we are highlighting dynamically Markdown code with Remarkable and a Web Component 🎉</p>
<h3>Cherry on the cake 🍒🎂</h3>
<p>DeckDeckGo is open source, therefore, you could have a look to the source code of our implementation on <a href="https://github.com/deckgo/deckdeckgo/blob/63a76e3a4d7b40ef0cb70dc30cae818da02d4d94/remote/src/app/components/app-notes/app-notes.tsx#L31" rel="noopener noreferrer external">GitHub</a>.</p>
<p>To infinity and beyond 🚀</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Create a Primefaces JSF project with Maven and Wildfly]]></title>
          <description><![CDATA[How to create a Primefaces and JSF project with Maven and Wildfly as a local server]]></description>
          <link>https://daviddalbusco.com/blog/create-a-primefaces-jsf-project-with-maven-and-wildfly/</link>
          <pubDate>Wed, 30 Oct 2019 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*w93lFGpSosbY9UblQNqUCA.jpeg" alt="" / loading="lazy"></p>
<p>I'm currently on my way to Fuerteventura to enjoy a week of holidays ☀️ Earlier this year, I found out that writing blog posts was a great way to use wisely my time spent traveling, that's why I'm repeating the experience.</p>
<p>When I thought about which subject I should write about earlier this morning (I don't generally have any plan in advance when it comes to writing) I came to the idea that it would be maybe interesting to share my favorite JSF trick (or hack, depends if you see the glass half empty or full). But before doing so, I had to initialize a clean blank project, that's why I've decided to first write this tutorial 😋</p>
<h3>But why a JSF project?</h3>
<p><img src="https://daviddalbusco.com/assets/images/1*evsRlXzetp7FdEOK2ynL1Q.gif" alt="" / loading="lazy"></p>
<p><em>But why a JSF project?</em></p>
<p>Honestly, I don't know exactly why anyone would be still interested to create a brand new JavaServer Faces API (JSF) project in 2019 or why would anyone even read this article 🤣. Not that I don't like Java, I still do and I still think it's a good match for certain types of projects, notably when it goes to the back- and middle-end layers but I would personally not use or advise it for the frontend part of any <strong>new</strong> projects.</p>
<p>That's why I'm really curious to hear your voice, why are you reading this article? Why are you looking to create a JSF project? Let me know with a comment 😁</p>
<h3>Getting started</h3>
<p>In this article we are going to follow the following steps:</p>
<ol>
<li>Create a new web project</li>
<li>Add a Wildfly development server to our tooling</li>
<li>Load and configure JSF</li>
<li>Implement Primefaces</li>
</ol>
<p>Let's get started 🚀</p>
<h3>1. Create a new project</h3>
<p>To create a new project, we are going to use <a href="https://maven.apache.org" rel="noopener noreferrer external">Apache Maven</a> and its <a href="https://maven.apache.org/archetypes/maven-archetype-webapp/" rel="noopener noreferrer external">web starter kit</a> or as it is described in its documentation &quot;an archetype which generates a sample Maven webapp project&quot;.</p>
<p>For this purpose we run the following command in a terminal:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">mvn</span><span style="color:#F1FA8C"> archetype:generate</span><span style="color:#BD93F9"> -DgroupId=com.jsfdemo.app</span><span style="color:#BD93F9"> -DartifactId=jsf-demo</span><span style="color:#BD93F9"> -DarchetypeArtifactId=maven-archetype-webapp</span><span style="color:#BD93F9"> -DarchetypeVersion=1.4</span><span style="color:#BD93F9"> -DinteractiveMode=false</span></span></code></pre><p>If everything went according plan, we should be able to change directory and compile the project without any errors:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#8BE9FD">cd</span><span style="color:#F1FA8C"> jsf-demo/</span><span style="color:#F8F8F2"> &#x26;&#x26; </span><span style="color:#50FA7B">mvn</span><span style="color:#F1FA8C"> package</span></span></code></pre><h3>2. Add a Wildfly development server to our tooling</h3>
<p><a href="https://wildfly.org" rel="noopener noreferrer external">WildFly</a>, formerly known as JBoss, is an application server authored by JBoss, now developed by <a href="https://www.redhat.com" rel="noopener noreferrer external">Red Hat</a>. Wildfly could be added to Maven's project thanks to the plugin <code>wildfly-maven-plugin</code>. Through its usage you could either interact with your local installed server or deploy one on the fly which is the option we are going to use.</p>
<p>To add it to our project, we are going now to edit our <code>pom.xml</code>. We add a new <code>&lt;dependency/&gt;</code> to our <code>&lt;dependencies/&gt;</code> and we add it as another <code>&lt;plugin/&gt;</code> too.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">dependency</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;</span><span style="color:#FF79C6">groupId</span><span style="color:#F8F8F2">>org.wildfly.plugins&#x3C;/</span><span style="color:#FF79C6">groupId</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;</span><span style="color:#FF79C6">artifactId</span><span style="color:#F8F8F2">>wildfly-maven-plugin&#x3C;/</span><span style="color:#FF79C6">artifactId</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;</span><span style="color:#FF79C6">version</span><span style="color:#F8F8F2">>2.0.1.Final&#x3C;/</span><span style="color:#FF79C6">version</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;</span><span style="color:#FF79C6">type</span><span style="color:#F8F8F2">>maven-plugin&#x3C;/</span><span style="color:#FF79C6">type</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">dependency</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">plugin</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">groupId</span><span style="color:#F8F8F2">>org.wildfly.plugins&#x3C;/</span><span style="color:#FF79C6">groupId</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">artifactId</span><span style="color:#F8F8F2">>wildfly-maven-plugin&#x3C;/</span><span style="color:#FF79C6">artifactId</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">version</span><span style="color:#F8F8F2">>2.0.1.Final&#x3C;/</span><span style="color:#FF79C6">version</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">plugin</span><span style="color:#F8F8F2">></span></span></code></pre><p>Once the configuration saved, we could build and install our project and run the application to test our setup with our browser respectively <a href="http://localhost:8080/" rel="noopener noreferrer external">http://localhost:8080/</a> should gives us access to its administration console and <a href="http://localhost:8080/jsf-demo" rel="noopener noreferrer external">http://localhost:8080/jsf-demo</a> to a default &quot;Hello World&quot; page.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">mvn</span><span style="color:#F1FA8C"> clean</span><span style="color:#F1FA8C"> install</span><span style="color:#F8F8F2"> &#x26;&#x26; </span><span style="color:#50FA7B">mvn</span><span style="color:#F1FA8C"> wildfly:run</span></span></code></pre><p><img src="https://daviddalbusco.com/assets/images/1*Bj1lhJuKOuWch0DICh7npA.png" alt="" / loading="lazy"></p>
<p><em>Administration console</em></p>
<p><img src="https://daviddalbusco.com/assets/images/1*nWVepJkORKK6_wp4w2EB3Q.png" alt="" / loading="lazy"></p>
<p><em>&quot;Hello World&quot; page</em></p>
<h4>Note</h4>
<p>This Wildfly plugin doesn't work, out of the box, offline. Even if I fetched everything and already ran my project before taking the plane, I was unable to run it during the flight.</p>
<h3>3. Load and configure JSF</h3>
<p>There are different implementations of the JavaServer Faces API (JSF). For the purpose of this tutorial we are going to use the one from <a href="https://www.oracle.com/technetwork/java/index.html" rel="noopener noreferrer external">Oracle</a> which we are going to add as a new <code>&lt;dependency/&gt;</code> to our <code>pom.xml</code>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">dependency</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">groupId</span><span style="color:#F8F8F2">>com.sun.faces&#x3C;/</span><span style="color:#FF79C6">groupId</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">artifactId</span><span style="color:#F8F8F2">>jsf-api&#x3C;/</span><span style="color:#FF79C6">artifactId</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">version</span><span style="color:#F8F8F2">>2.2.20&#x3C;/</span><span style="color:#FF79C6">version</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">dependency</span><span style="color:#F8F8F2">></span></span></code></pre><p>Beside the dependency, we also have to configure our application server to make it able to resolve <code>jsf</code> and <code>xhtml</code> target pages and to make these able to communicate with our beans. This configuration find place and in <code>src/main/webapp/WEB-INF/web.xml</code>:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;!</span><span style="color:#FF79C6">DOCTYPE</span><span style="color:#BD93F9;font-style:italic"> web-app</span><span style="color:#F8F8F2"> PUBLIC</span></span>
<span class="line"><span style="color:#F8F8F2"> "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"</span></span>
<span class="line"><span style="color:#F8F8F2"> "http://java.sun.com/dtd/web-app_2_3.dtd" ></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">web-app</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;</span><span style="color:#FF79C6">display-name</span><span style="color:#F8F8F2">>Archetype Created Web Application&#x3C;/</span><span style="color:#FF79C6">display-name</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;</span><span style="color:#FF79C6">context-param</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">param-name</span><span style="color:#F8F8F2">>javax.faces.PROJECT_STAGE&#x3C;/</span><span style="color:#FF79C6">param-name</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">param-value</span><span style="color:#F8F8F2">>Development&#x3C;/</span><span style="color:#FF79C6">param-value</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;/</span><span style="color:#FF79C6">context-param</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;</span><span style="color:#FF79C6">servlet</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">servlet-name</span><span style="color:#F8F8F2">>Faces Servlet&#x3C;/</span><span style="color:#FF79C6">servlet-name</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">servlet-class</span><span style="color:#F8F8F2">>javax.faces.webapp.FacesServlet&#x3C;/</span><span style="color:#FF79C6">servlet-class</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;/</span><span style="color:#FF79C6">servlet</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;</span><span style="color:#FF79C6">servlet-mapping</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">servlet-name</span><span style="color:#F8F8F2">>Faces Servlet&#x3C;/</span><span style="color:#FF79C6">servlet-name</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">url-pattern</span><span style="color:#F8F8F2">>*.jsf&#x3C;/</span><span style="color:#FF79C6">url-pattern</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;/</span><span style="color:#FF79C6">servlet-mapping</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;</span><span style="color:#FF79C6">servlet-mapping</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">servlet-name</span><span style="color:#F8F8F2">>Faces Servlet&#x3C;/</span><span style="color:#FF79C6">servlet-name</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">url-pattern</span><span style="color:#F8F8F2">>*.xhtml&#x3C;/</span><span style="color:#FF79C6">url-pattern</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;/</span><span style="color:#FF79C6">servlet-mapping</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">web-app</span><span style="color:#F8F8F2">></span></span></code></pre><p>Our application being configured, we could created our first Java bean, <code>HelloWorldBean.java</code>, in <code>src/main/java</code>. This class will be a simple backed bean which exposes a method to say hello to the world from Fuerteventura.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> java.io.Serializable;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> javax.inject.Named;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> javax.faces.view.ViewScoped;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#8BE9FD;font-style:italic">Named</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">helloworld</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#8BE9FD;font-style:italic">ViewScoped</span></span>
<span class="line"><span style="color:#FF79C6">public</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> HelloWorldBean</span><span style="color:#FF79C6"> implements</span><span style="color:#8BE9FD;font-style:italic"> Serializable</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    public</span><span style="color:#8BE9FD;font-style:italic"> String</span><span style="color:#50FA7B"> getMessage</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Hello World from Fuertefentura</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Finally, to present this message, we add a new servlet, <code>hello.xhtml</code>, in <code>src/main/webapp</code> which uses our above bean.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;!</span><span style="color:#FF79C6">DOCTYPE</span><span style="color:#50FA7B;font-style:italic"> html</span><span style="color:#50FA7B;font-style:italic"> PUBLIC</span><span style="color:#F1FA8C"> "-//W3C//DTD XHTML 1.0 Transitional//EN"</span><span style="color:#F1FA8C"> "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">html</span><span style="color:#50FA7B;font-style:italic"> xmlns</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">http://www.w3.org/1999/xhtml</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">head</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">title</span><span style="color:#F8F8F2">>#{helloworld.message}&#x3C;/</span><span style="color:#FF79C6">title</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">head</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">body</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        #{helloworld.message}</span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">body</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">html</span><span style="color:#F8F8F2">></span></span></code></pre><p>As before, to try out our code, we build our project and run the server again.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">mvn</span><span style="color:#F1FA8C"> clean</span><span style="color:#F1FA8C"> install</span><span style="color:#F8F8F2"> &#x26;&#x26; </span><span style="color:#50FA7B">mvn</span><span style="color:#F1FA8C"> wildfly:run</span></span></code></pre><p>If we access <a href="http://localhost:8080/jsf-demo/hello.xhtml" rel="noopener noreferrer external">http://localhost:8080/jsf-demo/hello.xhtml</a> with our browser, we should now see the friendly message as title and content of the page.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*c6cxLyLRRScp0nTWgya9zQ.png" alt="" / loading="lazy"></p>
<p><em>&quot;Hello World form Fuerteventura&quot;</em></p>
<h3>4. Implement Primefaces</h3>
<p><a href="https://www.primefaces.org" rel="noopener noreferrer external">Primefaces</a> is an UI toolkit build at the top of JSF. It add multiple components like table, panel, dialog, etc. You might not need it but I do need it for my next blog post, therefore, let's add it to our project too 😇</p>
<p>We proceed as the pasts steps by adding a new <code>&lt;dependency/&gt;</code> to our <code>pom.xml</code>:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">dependency</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">groupId</span><span style="color:#F8F8F2">>org.primefaces&#x3C;/</span><span style="color:#FF79C6">groupId</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">artifactId</span><span style="color:#F8F8F2">>primefaces&#x3C;/</span><span style="color:#FF79C6">artifactId</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">version</span><span style="color:#F8F8F2">>7.0&#x3C;/</span><span style="color:#FF79C6">version</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">dependency</span><span style="color:#F8F8F2">></span></span></code></pre><p>No special configuration is needed and out of the box we should already be able to use their components. We could for example extend our <code>hello.xhtml</code> with an horizontal panel.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;!</span><span style="color:#FF79C6">DOCTYPE</span><span style="color:#50FA7B;font-style:italic"> html</span><span style="color:#50FA7B;font-style:italic"> PUBLIC</span><span style="color:#F1FA8C"> "-//W3C//DTD XHTML 1.0 Transitional//EN"</span><span style="color:#F1FA8C"> "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">html</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    xmlns</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">http://www.w3.org/1999/xhtml</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    xmlns:h</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">http://java.sun.com/jsf/html</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    xmlns:p</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">http://primefaces.org/ui</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">h:head</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">title</span><span style="color:#F8F8F2">>#{helloworld.message}&#x3C;/</span><span style="color:#FF79C6">title</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">h:head</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">h:body</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">p:panel</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            id</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">horizontal</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            header</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Horizontal Toggle</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            toggleable</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">true</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            toggleOrientation</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">horizontal</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">        ></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">h:panelGrid</span><span style="color:#50FA7B;font-style:italic"> columns</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">2</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> cellpadding</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">10</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF79C6">img</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                    src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://images.unsplash.com/photo-1545289415-50dff9405935?ixlib=rb-1.2.1</span><span style="color:#BD93F9">&#x26;amp;</span><span style="color:#F1FA8C">ixid=eyJhcHBfaWQiOjEyMDd9</span><span style="color:#BD93F9">&#x26;amp;</span><span style="color:#F1FA8C">auto=format</span><span style="color:#BD93F9">&#x26;amp;</span><span style="color:#F1FA8C">fit=crop</span><span style="color:#BD93F9">&#x26;amp;</span><span style="color:#F1FA8C">w=250</span><span style="color:#BD93F9">&#x26;amp;</span><span style="color:#F1FA8C">q=80</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">                /></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">h:outputText</span><span style="color:#50FA7B;font-style:italic"> value</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">#{helloworld.message}</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2"> /></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">h:panelGrid</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">p:panel</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">h:body</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">html</span><span style="color:#F8F8F2">></span></span></code></pre><p>We could again build and start our application server to try out our implementation.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">mvn</span><span style="color:#F1FA8C"> clean</span><span style="color:#F1FA8C"> install</span><span style="color:#F8F8F2"> &#x26;&#x26; </span><span style="color:#50FA7B">mvn</span><span style="color:#F1FA8C"> wildfly:run</span></span></code></pre><p>Finally we could check <a href="http://localhost:8080/jsf-demo/hello.xhtml" rel="noopener noreferrer external">http://localhost:8080/jsf-demo/hello.xhtml</a> in our browser to check if everything is correctly running.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*hBnP4yj5wIgn9fE-NJc-bA.png" alt="" / loading="lazy"><br />
<em>“Hello World from Fuerteventura” presented in a Primefaces’ panel</em></p>
<p>Voilà, we did it! We created a Primefaces JSF project with Maven and Widfly and are now able to make some testing 🎉 Moreover, even if I'm not looking forward to going home, I will be able to use this blank new project as a starter kit for my next article 😆</p>
<p>To infinity and beyond 🚀</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Integrate the same QR code Web Component in React, Angular or Vue]]></title>
          <description><![CDATA[How to integrate the same QR code Web Component developed with StencilJS in React, Angular or Vue]]></description>
          <link>https://daviddalbusco.com/blog/integrate-the-same-qr-code-web-component-in-react-angular-or-vue/</link>
          <pubDate>Tue, 08 Oct 2019 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*fqN7UAsguHXdG2ojvxgvJA.jpeg" alt="" / loading="lazy"><br />
<em>Photo by <a href="https://unsplash.com/@aka_opex?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Mitya Ivanov</a> on <a href="https://unsplash.com/s/photos/qr-codes?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>One of the cool asset of Web Components is the fact that they could be integrated in any modern web applications, regardless if these are using a framework or not. As I had to implement some improvements in the QR code Web Component we are using in <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>, as thought it would be interesting to display how it could be used across frameworks. Furthermore, as I’m not yet that experienced with React and Vue, it’s also a fun way for me to improve a bit my skills.</p>
<h3>Introduction</h3>
<p>The Web Component we are going to use is a wrapper I’ve implemented with <a href="https://stenciljs.com/" rel="noopener noreferrer external">StencilJS</a> around the <a href="https://github.com/kazuhikoarase/qrcode-generator" rel="noopener noreferrer external">generator</a> developed by <a href="https://github.com/kazuhikoarase" rel="noopener noreferrer external">Kazuhiko Arase</a>. Its package name is <code>@deckdeckgo/qrcore</code>.</p>
<p>In the following chapters we are going to integrate the component in three different starter applications developed with <a href="https://reactjs.org" rel="noopener noreferrer external">React</a>, <a href="https://angular.io" rel="noopener noreferrer external">Angular</a> and <a href="https://vuejs.org" rel="noopener noreferrer external">Vue</a>.</p>
<p>Regardless of the framework, the process will always be the same:</p>
<ol>
<li>Create a new application using a starter kit</li>
<li>Install the component with <a href="https://www.npmjs.com" rel="noopener noreferrer external">npm</a></li>
<li>Import the component in the application</li>
<li>Render the component</li>
<li>Start the application</li>
<li>Test it in the browser</li>
</ol>
<p>So, let’s get started 🏁</p>
<h3>React</h3>
<blockquote>
<p>A JavaScript library for building user interfaces (source: <a href="https://reactjs.org" rel="noopener noreferrer external">https://reactjs.org</a>).</p>
</blockquote>
<h4>1. Create a new application using a starter kit</h4>
<p>To create a new React application we are going to use the official CLI project <a href="https://github.com/facebook/create-react-app" rel="noopener noreferrer external">create-react-app</a> and are going to run the following command (“demo-qrcode-react” being the name of our project):</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npx</span><span style="color:#F1FA8C"> create-react-app</span><span style="color:#F1FA8C"> demo-qrcode-react</span></span></code></pre><p>The process might take a while (more than 1'500 dependencies have to be fetched) but once done, we can jump into our newly created project:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#8BE9FD">cd</span><span style="color:#F1FA8C"> demo-qrcode-react</span></span></code></pre><h4>2. Install the component with npm</h4>
<p>We could now add the Web Component to the project running the following command:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> install</span><span style="color:#F1FA8C"> @deckdeckgo/qrcode</span><span style="color:#BD93F9"> --save</span></span></code></pre><p>Note that the component is agnostic and therefore no other dependencies will have to be fetched and added to the project.</p>
<h4>3. Import the component in the application</h4>
<p>In all examples of this blog post, we are going to import the component in the main application. It’s worth to notice that components build and shipped with StencilJS are lazy loaded and therefore optimized to match the best performances of your applications.</p>
<p>In the particular case of React, we are going to import the component in <code>src/index.js</code> and proceed as displayed in the <a href="https://stenciljs.com/docs/react" rel="noopener noreferrer external">documentation</a> respectively importing it with a loader and defining it as a custom element.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> React </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> ReactDOM </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">react-dom</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./index.css</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> App </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./App</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#BD93F9"> *</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> serviceWorker </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./serviceWorker</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// A. We import our loader</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { applyPolyfills, defineCustomElements } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@deckdeckgo/qrcode/dist/loader</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">ReactDOM.</span><span style="color:#50FA7B">render</span><span style="color:#F8F8F2">(&#x3C;</span><span style="color:#8BE9FD;font-style:italic">App</span><span style="color:#F8F8F2"> />, document.</span><span style="color:#50FA7B">getElementById</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">root</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">));</span></span>
<span class="line"><span style="color:#F8F8F2">serviceWorker.</span><span style="color:#50FA7B">unregister</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// B. We load our component</span></span>
<span class="line"><span style="color:#50FA7B">applyPolyfills</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">then</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    defineCustomElements</span><span style="color:#F8F8F2">(window);</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><h4>4. Render the component</h4>
<p>Our configuration is all set, we could now have a bit of fun and strictly speaking implement the component in our application. For that purpose we are going to add it to the main <code>src/App.js</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> React </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">react</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./App.css</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">function</span><span style="color:#50FA7B"> App</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> className</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">App</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#FF79C6">header</span><span style="color:#50FA7B;font-style:italic"> className</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">App-header</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#8BE9FD;font-style:italic">deckgo-qrcode</span><span style="color:#50FA7B;font-style:italic"> content</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://reactjs.org</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">deckgo-qrcode</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#FF79C6">header</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> default</span><span style="color:#F8F8F2"> App;</span></span></code></pre><h4>5. Start the application</h4>
<p>Our component has been imported and integrated, we could now test our implementation and run our app:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> run</span><span style="color:#F1FA8C"> start</span></span></code></pre><h4>6. Test it in the browser</h4>
<p>If everything works according plan, our application and QR code should be deployed and accessible in our browser at the address <code>http://localhost:3000</code> .</p>
<p><img src="https://daviddalbusco.com/assets/images/1*e_xIvjI2ZdELhj36u1LlfA.png" alt="" / loading="lazy"></p>
<p><em>The result of our test with React</em></p>
<h3>Angular</h3>
<blockquote>
<p>One framework. Mobile &amp; desktop. (source: <a href="https://angular.io" rel="noopener noreferrer external">https://angular.io</a>).</p>
</blockquote>
<h4>1. Create a new application using a starter kit</h4>
<p>To create a new application using Angular, we first have to install their CLI globally on our computer.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> install</span><span style="color:#BD93F9"> -g</span><span style="color:#F1FA8C"> @angular/cli</span></span></code></pre><p>Once installed, we can proceed and create our starter application (“demo-qrcode-angular” being the name of this project).</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">ng</span><span style="color:#F1FA8C"> new</span><span style="color:#F1FA8C"> demo-qrcode-angular</span></span></code></pre><p>Note that for our test, the routing isn’t mandatory and the styling method isn’t relevant. The operation will again take a bit of time as almost 1'500 dependencies have to be fetched. Once done, we jump into our newly created project.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#8BE9FD">cd</span><span style="color:#F1FA8C"> demo-qrcode-angular</span></span></code></pre><h4>2. Install the component with npm</h4>
<p>Like before, we install the component from npm using the following command:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> install</span><span style="color:#F1FA8C"> @deckdeckgo/qrcode</span><span style="color:#BD93F9"> --save</span></span></code></pre><h4>3. Import the component in the application</h4>
<p>When it comes to Angular, I’m not sure if it is still mandatory, but before stricto sensu importing it, we first have to tell Angular to allow the use of custom elements schema. To achieve that goal, we modify <code>src/app/app.module.ts</code>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { BrowserModule } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@angular/platform-browser</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { CUSTOM_ELEMENTS_SCHEMA, NgModule } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { AppComponent } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./app.component</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">NgModule</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    declarations</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#50FA7B;font-style:italic">AppComponent</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">    imports</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#50FA7B;font-style:italic">BrowserModule</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">    providers</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [],</span></span>
<span class="line"><span style="color:#F8F8F2">    bootstrap</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#50FA7B;font-style:italic">AppComponent</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">    schemas</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [CUSTOM_ELEMENTS_SCHEMA]</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> AppModule</span><span style="color:#F8F8F2"> {}</span></span></code></pre><p>Note that <code>CUSTOM_ELEMENTS_SCHEEMA</code> needs to be included in any module that uses custom elements.</p>
<p>Finally we are going to import our component as displayed on the <a href="https://stenciljs.com/docs/angular" rel="noopener noreferrer external">documentation</a>, by using the loader and by defining our element in the <code>src/main.ts</code> application.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { enableProdMode } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { platformBrowserDynamic } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@angular/platform-browser-dynamic</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { AppModule } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./app/app.module</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { environment } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./environments/environment</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// A. We import our loader</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { applyPolyfills, defineCustomElements } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@deckdeckgo/qrcode/dist/loader</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">if</span><span style="color:#F8F8F2"> (environment.production) {</span></span>
<span class="line"><span style="color:#50FA7B">    enableProdMode</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">platformBrowserDynamic</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">bootstrapModule</span><span style="color:#F8F8F2">(AppModule)</span></span>
<span class="line"><span style="color:#F8F8F2">    .</span><span style="color:#50FA7B">catch</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">err</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(err));</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// B. We load our component</span></span>
<span class="line"><span style="color:#50FA7B">applyPolyfills</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">then</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    defineCustomElements</span><span style="color:#F8F8F2">(window);</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><h4>4. Render the component</h4>
<p>We can now implement the component in our application. Angular being based on HTML templates, we are going to add it in the main one of the application <code>src/app/app.component.html</code> , at the top of the page’s content.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> class</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">content</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> role</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">main</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">deckgo-qrcode</span><span style="color:#50FA7B;font-style:italic"> content</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://angular.io</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">deckgo-qrcode</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span></code></pre><h4>5. Start the application</h4>
<p>To run our application we can again run the default command.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> run</span><span style="color:#F1FA8C"> start</span></span></code></pre><h4>6. Test it in the browser</h4>
<p>Our application and QR code should now be deployed and accessible in our browser at the address <code>http://localhost:4200</code> .</p>
<p><img src="https://daviddalbusco.com/assets/images/1*_Vbu1yVm5naiR7QQtAom-g.png" alt="" / loading="lazy"></p>
<p><em>The result of our test with Angular</em></p>
<h3>Vue</h3>
<blockquote>
<p>The Progressive JavaScript Framework (source: <a href="https://vuejs.org" rel="noopener noreferrer external">https://vuejs.org</a>).</p>
</blockquote>
<h4>1. Create a new application using a starter kit</h4>
<p>In order to create a new application with Vue, we would be able to use a boiler plate but, to proceed as we did with Angular, we are going to install their CLI globally on our machine first.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> install</span><span style="color:#BD93F9"> -g</span><span style="color:#F1FA8C"> @vue/cli</span></span></code></pre><p>We proceed then with the creation of our application (“demo-qrcode-vue” being the name of this project).</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">vue</span><span style="color:#F1FA8C"> create</span><span style="color:#F1FA8C"> demo-qrcode-vue</span></span></code></pre><p>It might take a bit time (but a bit less time than before as only around 1'300 dependencies have to fetched). Once everything installed, we jump into our newly created project.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#8BE9FD">cd</span><span style="color:#F1FA8C"> demo-qrcode-vue</span></span></code></pre><h4>2. Install the component with npm</h4>
<p>We can install the component from npm the exact same way as we did with the two other frameworks:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> install</span><span style="color:#F1FA8C"> @deckdeckgo/qrcode</span><span style="color:#BD93F9"> --save</span></span></code></pre><h4>3. Import the component in the application</h4>
<p>In addition to importing and defining our component using the loader as we did before, we also have to instruct the Vue compiler to ignore our custom element tag. For that purpose we should modify <code>src/main.js</code> as displayed in the <a href="https://stenciljs.com/docs/vue" rel="noopener noreferrer external">documentation</a>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> Vue </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">vue</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> App </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">./App.vue</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// A. We import our loader</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { applyPolyfills, defineCustomElements } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">@deckdeckgo/qrcode/dist/loader</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">Vue.config.productionTip </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// C. Tell  the compiler to ignore our custom element tag</span></span>
<span class="line"><span style="color:#F8F8F2">Vue.config.ignoredElements </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> [</span><span style="color:#FF5555">/</span><span style="color:#F1FA8C">deckgo-</span><span style="color:#BD93F9">\w</span><span style="color:#FF79C6">*</span><span style="color:#FF5555">/</span><span style="color:#F8F8F2">];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6;font-weight:bold">new</span><span style="color:#50FA7B"> Vue</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#50FA7B">    render</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">h</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> h</span><span style="color:#F8F8F2">(App)</span></span>
<span class="line"><span style="color:#F8F8F2">}).</span><span style="color:#50FA7B">$mount</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">#app</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// B. We load our component</span></span>
<span class="line"><span style="color:#50FA7B">applyPolyfills</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">then</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">    defineCustomElements</span><span style="color:#F8F8F2">(window);</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><h4>4. Render the component</h4>
<p>We would now be able to add our component to the main <code>src/App.vue</code> template in order to render the QR code in our application but, as a component for the first page was already created with the starter kit creation, let’s rather add our component at the begin of root element in the template <code>src/components/HelloWorld.vue</code>.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">template</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> class</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">hello</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">deckgo-qrcode</span><span style="color:#50FA7B;font-style:italic"> content</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://vuejs.org</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">deckgo-qrcode</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">div</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">template</span><span style="color:#F8F8F2">></span></span></code></pre><h4>5. Start the application</h4>
<p>There is no default “start” target in the Vue starter kit, therefore let’s run the following command to launch our application:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">npm</span><span style="color:#F1FA8C"> run</span><span style="color:#F1FA8C"> serve</span></span></code></pre><h4>6. Test it in the browser</h4>
<p>All right, final test 🎉 Our application and QR code should now be deployed and accessible in our browser at the address <code>http://localhost:8080.</code></p>
<p><img src="https://daviddalbusco.com/assets/images/1*mvCWPtw9p-Da-doxk6Cedw.png" alt="" / loading="lazy"></p>
<p><em>The result of our test with Vue</em></p>
<h3>Conclusion</h3>
<p>When it comes to me, I think that the most interesting takeover from this small experience is noticing that importing and using Web Component is almost exactly the same approach and experience regardless of the frameworks. Moreover than the obvious business advantages of having the exact same component and code working seamlessly across technologies, it also gives me an interesting feeling that they, Web Components, might be the missing chain which would allow me to jump from a project to another one more easily. I don’t know if you share the same feeling, I would be happy to hear your thoughts.</p>
<h3>Cherry on the cake 🍒🎂</h3>
<p>The QR code Web Component we just used offers many styling options (using CSS4 variables) and even the ability to display a logo over it. Moreover it is open source. Therefore, if you need such a component or have idea of improvements, go for it and get started with its <a href="https://docs.deckdeckgo.com/components/qrcode/" rel="noopener noreferrer external">documentation</a>.</p>
<p>To infinity and beyond 🚀</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Introducing DeckDeckGo: the web open source editor for presentations]]></title>
          <description><![CDATA[Introducing DeckDeckGo: the web open source editor for presentations]]></description>
          <link>https://daviddalbusco.com/blog/introducing-deckdeckgo-the-web-open-source-editor-for-presentations/</link>
          <pubDate>Tue, 24 Sep 2019 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*TmZb15Yzu3mGVG7uMkHvHw.png" alt="" / loading="lazy"></p>
<p>We are very happy and excited to share with you today our web open source editor for presentations: <a href="https://beta.deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a></p>
<p>I still almost not believe that we managed to reach such a milestone. It took us a couple of months this year to develop it just for fun at nights and on weekends, but yes, we did it 🎉</p>
<p>Before going further we would like first to thank all our off- and online friends, communities, contributors and early testers which always gave us plenty of motivation to develop our, as I like to call it, “pet” project. You are truly awesome ❤️</p>
<h3>But why yet again another editor for presentations?</h3>
<p><img src="https://daviddalbusco.com/assets/images/1*Ireg4JWSR6Se4c_UP8iJ6w.gif" alt="" / loading="lazy"></p>
<p><em>Sure why? Seriously why?</em></p>
<p>We are fully aware that no one, absolutely no one, asked for yet again another editor for presentations and that even probably there is no such need. But you know what? We are fine with that. Of course we would love and hope that our editor and concept will be useful to some persons, but even if we might cry a bit if no one will use it, we are (kind of) ok with that.</p>
<p>We develop our project because we are, well, nerds and because we use this experience to improve our skills. Furthermore, we are engineers you know, we like to implement products from scratch until launch 😉</p>
<h3>So what makes it different?</h3>
<p><img src="https://daviddalbusco.com/assets/images/1*MNW_kIkOJqNvRGBRFNIbfg.gif" alt="" / loading="lazy"></p>
<p><em>Everything.</em></p>
<p>Of course I’m joking, not everything is different, it’s even became a private joke between us. As soon as something is a bit new we say to each other “Mais mec, c’est le future” 😹</p>
<p>That being said and as I briefly introduced it above, we had some, I hope you will find too, interesting ideas.</p>
<h4>Cloud solution</h4>
<p><img src="https://daviddalbusco.com/assets/images/1*9odqQrlsXeP0zykRe3lyaQ.gif" alt="" / loading="lazy"></p>
<p><em>My mum better understood “a cloud solution” than “we developed it with the web”</em></p>
<p>DeckDeckGo is a cloud solution. It works everywhere on any devices, <strong>projectors</strong>, <strong>desktops</strong>, <strong>mobiles</strong> or <strong>tablets</strong> without any prior installation and without data save locally 🎥🖥️💻📱☁️</p>
<p>There is by the way even no “Save” button in our editor, it does the job for you and save your data when these have to be saved 😁</p>
<h4>Use an app to share apps</h4>
<p><img src="https://daviddalbusco.com/assets/images/1*4sMOauhgBsqV4wZzJ3fZqA.gif" alt="" / loading="lazy"></p>
<p><em>Use an app to share other apps</em></p>
<p>No export to PDF (at least not yet), therefore you may ask your self: How my gosh David, how I’m going to share my presentation then?</p>
<p>Well, here’s a cool thing: DeckDeckGo package and publish every single presentation you would like to <strong>share as a standalone application.</strong> Most precisely as Progressive Web Apps. Basically you are not going to send a PDF to your friends a colleagues but you are going to send them a link to your app respectively to your presentation which is compatible with any device’s screen size and SEO friendly 🚀</p>
<h4>Online feed</h4>
<p><img src="https://daviddalbusco.com/assets/images/1*QyNE4kixGi60FyZ8Y0we2Q.gif" alt="" / loading="lazy"></p>
<p><em>Discover and share presentations</em></p>
<p>DeckDeckGo isn’t “just” an editor. It was actually developed to be also an <strong>online feed</strong>. A place where presentations can be discovered, shared and ultimately, if we let our selves dream a bit about the future, are indexed according your interests and maybe even can be discussed.</p>
<h3>Features</h3>
<p>Moreover, more than being able to develop slides with our editor, we also thought that adding some handy features would make the user experience a bit more enjoyable.</p>
<h4>Full screen edition</h4>
<p>Are you constantly switching between full screen and normal mode while editing your deck for your next presentation? Or did it you had more than one to correct a typo a couple of seconds before your talk? With our editor, your presentation is editable even in full screen mode.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*QM9ylLr7Tzj8ZYE-ZJUXoA.gif" alt="" / loading="lazy"></p>
<p><em>More than edition, per design all features are available in full screen mode too</em></p>
<h4>Unsplash</h4>
<p>We have integrated <a href="https://unsplash.com/" rel="noopener noreferrer external">Unsplash</a> to our tool to let your find and use stock photos easily. The editor keeps also track of the last 10 media you would have used for even a quicker access.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*_rEqbFfwTocrElky3mLJQA.gif" alt="" / loading="lazy"><br />
<em>Unsplash is integrated</em></p>
<h4>Tenor</h4>
<p>Gifs are life (probably). Same as the stock photo, <a href="https://tenor.com/" rel="noopener noreferrer external">Tenor</a> is integrated, to let you find and integrate Gifs easily. There is even a special template which takes care of fitting the animated content to the all screen.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*bsyb3eakx7dcvI2CftHEzw.gif" alt="" / loading="lazy"></p>
<p><em>Gifs are life</em></p>
<h4>Youtube</h4>
<p>I was a bit more lazy on the integration of <a href="https://www.youtube.com" rel="noopener noreferrer external">Youtube</a> videos, we didn’t interconnected any API, but still, it’s pretty damn easy to add any videos from that source to your deck. Cherry on top, you could start and pause the videos remotely with our app too.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*lyVzJnkv2MzOKCCRaSfkMg.gif" alt="" / loading="lazy"></p>
<p><em>Drop Youtube videos in your presentation</em></p>
<h4>Remote control</h4>
<p>Out of the box, without any special hardware, any presentations could be remote controlled with our “remote control” application. It handles currently actions like swiping slides, displaying notes, drawing on the presentation and even offers a countdown feature. We are also taking advantages of QR codes in order to establish easily the connection between the controller and the decks.</p>
<iframe width="280" height="158" src="https://www.youtube.com/embed/3o3oGBTTRSs" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe
<br/>
<p><em>Hey, it’s me, David 👋</em></p>
<h3>Technical fun facts</h3>
<p>I won’t deep to dive too much in the technical facts, as I hope that we will find time in the future to share articles about them, but to summarize, our application is developed with <a href="https://stenciljs.com" rel="noopener noreferrer external">StencilJS</a> Web Components. On the other side, our publication engine is developed with <a href="https://www.haskell.org" rel="noopener noreferrer external">Haskell</a>, <a href="https://nixos.org/nix/" rel="noopener noreferrer external">Nix</a> and <a href="https://www.terraform.io" rel="noopener noreferrer external">Terraform</a> 🚀</p>
<p>We are using <a href="https://firebase.google.com/" rel="noopener noreferrer external">Google Firebase</a> for the authentication and to store the data (data you are editing and as storage) and are using <a href="https://aws.amazon.com/" rel="noopener noreferrer external">Amazon AWS</a> to deploy online the publications you would share.</p>
<h3>Open source</h3>
<p>DeckDeckGo is open source, it’s in our DNA, sharing is caring. All our libraries are published under MIT license and our applications and infrastructure are published under AGPLv3 and above license.</p>
<p>Of course we are always looking for new contributors. We would love to hear from you, don’t hesitate to join us on our <a href="https://join.slack.com/t/deckdeckgo/shared_invite/enQtNzM0NjMwOTc3NTI0LTBlNmFhODNhYmRkMWUxZmU4ZTQ2MDJiNjlmYWZiODNjMDU5OGRjYThlZmZjMTc5YmQ3MzUzMDlhMzk0ZDgzMDY" rel="noopener noreferrer external">slack channel</a> or on <a href="https://github.com/deckgo/deckdeckgo" rel="noopener noreferrer external">GitHub</a> 🙏</p>
<h3>Developer mindset</h3>
<p>Something I particularly like in our project is that both the editor and our developer starter kit are using the exact same Web Components as engine 🤪 Any improvements in these libraries and applications are then automatically echoed to anyone regardless if you are using the graphical editor or are coding your own presentation using HTML or markdown, it’s kind of the cherry on the cake 🍒🎂</p>
<h3>Get started now 🔥</h3>
<p>A picture speaks a thousand words, get started now to create your next presentation: <a href="https://deckdeckgo.com" rel="noopener noreferrer external">https://deckdeckgo.com</a></p>
<p>To infinity and beyond 🚀</p>
<p>David</p>
<p>P.S.: Background photo of the cover by <a href="https://unsplash.com/@wisconsinpictures?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">wisconsinpictures</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Debounce with vanilla JavaScript or RxJS]]></title>
          <description><![CDATA[Debounce with vanilla JavaScript or RxJS]]></description>
          <link>https://daviddalbusco.com/blog/debounce-with-vanilla-javascript-or-rxjs/</link>
          <pubDate>Wed, 18 Sep 2019 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*710b8KhbLG3vPrV2cL-3dw.jpeg" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@barnabartis?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Barna Bartis</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I recently had to clean the code we are using in <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a> and had notably to refactor singleton methods to stateless functions. One of these gave me a harder time and that's why, guess what, I came to the idea of this new blog post 😅</p>
<h3>What is debouncing?</h3>
<p><img src="https://daviddalbusco.com/assets/images/1*L2pWuWEFxqMLUPGnmumuaw.gif" alt="" / loading="lazy"></p>
<p><em>Sure, what’s “debouncing” ?</em></p>
<p>Let’s say you have implemented an <code>&lt;input/&gt;</code> in your application which triggers an update into your database each time its content change. For performance reason and maybe even for cost reason (if for example you are using <a href="https://cloud.google.com/firestore/pricing" rel="noopener noreferrer external">Google Firestore</a>) you might not want to trigger a database update every single time a keyboard key is hit but rather perform a save only when needed. For example you might want to only perform the save when the user would mark a pause or when she/he has finished her/his interaction with the component.</p>
<p>Likewise, you may have a function in your application, which might be called multiple times in a row, for which you would rather like to consider only the last call.</p>
<p>That is what debouncing is for me, to make sure that a method is not called too often.</p>
<h3>Debounce time</h3>
<p>Commonly, in order to detect which functions should effectively be triggered, a delay between calls is observed. For example, if we are debouncing a function with a debounce time of 300ms, as soon as, or more than, 300ms between two calls are observed, the function will be triggered.</p>
<h3>Vanilla Javascript</h3>
<p><img src="https://daviddalbusco.com/assets/images/1*1kXFGBTN-o1VZ8nxRn5yjw.gif" alt="" / loading="lazy"></p>
<p><em>setTimeout and clearTimeout working together</em></p>
<p>There is currently no platform implementation of a standard “debouncing function” supported across browsers (correct me if I am wrong of course 😅). Fortunately, Javascript provide both the ability to delay a function’s call using <code>setTimeout</code> and to cancel it using <code>clearTimeout</code> which we could combine in order to implement our own solution.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> debounce</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">func</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Function</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">timeout</span><span style="color:#FF79C6">?:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">    let</span><span style="color:#F8F8F2"> timer</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#FF79C6"> |</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">...</span><span style="color:#FFB86C;font-style:italic">args</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> any</span><span style="color:#F8F8F2">[]) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#50FA7B"> next</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#50FA7B"> func</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">...</span><span style="color:#F8F8F2">args);</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (timer) {</span></span>
<span class="line"><span style="color:#50FA7B">            clearTimeout</span><span style="color:#F8F8F2">(timer);</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">        timer </span><span style="color:#FF79C6">=</span><span style="color:#50FA7B"> setTimeout</span><span style="color:#F8F8F2">(next, timeout </span><span style="color:#FF79C6">></span><span style="color:#BD93F9"> 0</span><span style="color:#FF79C6"> ?</span><span style="color:#F8F8F2"> timeout </span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 300</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>In the above code, our function (the one we effectively want to perform, passed as parameter <code>func</code> ) is going to be delayed (<code>setTimeout</code>). Before effectively doing so, we first check if it was not already called before (using the <code>timer</code> reference to the previous call) and if it was, we cancel this previous call (<code>clearTimeout</code>) before effectively delaying our target.</p>
<p>We could for example validate this implementation with a simple test. We could call multiple times in a row a function which log a string to the console. If everything works well, the output should occur only one time.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#50FA7B"> myFunction</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Function</span><span style="color:#FF79C6"> =</span><span style="color:#50FA7B"> debounce</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Triggered only once</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">myFunction</span><span style="color:#F8F8F2">(); </span><span style="color:#6272A4">// Cleared</span></span>
<span class="line"><span style="color:#50FA7B">myFunction</span><span style="color:#F8F8F2">(); </span><span style="color:#6272A4">// Cleared</span></span>
<span class="line"><span style="color:#50FA7B">myFunction</span><span style="color:#F8F8F2">(); </span><span style="color:#6272A4">// Cleared</span></span>
<span class="line"><span style="color:#50FA7B">myFunction</span><span style="color:#F8F8F2">(); </span><span style="color:#6272A4">// Cleared</span></span>
<span class="line"><span style="color:#50FA7B">myFunction</span><span style="color:#F8F8F2">(); </span><span style="color:#6272A4">// Performed and will output: Triggered only once</span></span></code></pre><p>If you wish to observe and test this in action, give a try to this <a href="https://codepen.io/peterpeterparker/pen/WNegLNb" rel="noopener noreferrer external">Codepen</a>.</p>
<h3>RxJS</h3>
<p><img src="https://daviddalbusco.com/assets/images/1*N3XGY3qwrM86jfr-WdqbHg.gif" alt="" / loading="lazy"></p>
<p><em>Good dog helping with the cleaning</em></p>
<p>The above solution with vanilla Javascript is pretty cool but what about achieving the same result using <a href="https://rxjs-dev.firebaseapp.com" rel="noopener noreferrer external">RxJS</a> (the Reactive Extensions Library for JavaScript)? That would be pretty slick isn’t it? Lucky us, RxJS offers out of the box a solution to debounce easily functions using Observables. Moreover, in my point of view, this solution is a bit cleaner and more readable.</p>
<p>The RxJS function we are going to use is <a href="https://rxjs-dev.firebaseapp.com/api/operators/debounceTime" rel="noopener noreferrer external">debounceTime</a>. As explained in the documentation, it delays values emitted by a source Observable, but drops previous pending delayed emissions if a new value arrives on the source Observable. To reproduce the same example as above and to create an observable, we could for example use a <code>Subject</code> and triggers multiple times in a row <code>next()</code> . If everything goes according plan, again, we should find only a single output in the console.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> mySubject</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Subject</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">void</span><span style="color:#F8F8F2">> </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Subject</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">subject.</span><span style="color:#50FA7B">pipe</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B">debounceTime</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">300</span><span style="color:#F8F8F2">)).</span><span style="color:#50FA7B">subscribe</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Triggered only once</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">mySubject.</span><span style="color:#50FA7B">next</span><span style="color:#F8F8F2">(); </span><span style="color:#6272A4">// Cleared</span></span>
<span class="line"><span style="color:#F8F8F2">mySubject.</span><span style="color:#50FA7B">next</span><span style="color:#F8F8F2">(); </span><span style="color:#6272A4">// Cleared</span></span>
<span class="line"><span style="color:#F8F8F2">mySubject.</span><span style="color:#50FA7B">next</span><span style="color:#F8F8F2">(); </span><span style="color:#6272A4">// Cleared</span></span>
<span class="line"><span style="color:#F8F8F2">mySubject.</span><span style="color:#50FA7B">next</span><span style="color:#F8F8F2">(); </span><span style="color:#6272A4">// Cleared</span></span>
<span class="line"><span style="color:#F8F8F2">mySubject.</span><span style="color:#50FA7B">next</span><span style="color:#F8F8F2">(); </span><span style="color:#6272A4">// Performed and will output: Triggered only once</span></span></code></pre><p>That’s it, nothing more nothing else. No custom functions to write, RxJS just solves the debouncing for us.</p>
<p>If you wish to give it a try in action too, have a look at this other <a href="https://codepen.io/peterpeterparker/pen/ZEzqXPw" rel="noopener noreferrer external">Codepen</a>.</p>
<p><em>Notabene: in the above example I did not, for simplicity reason, took care of unsubscribing the Observable. Obviously if you would use this solution in a real application, please be careful about this.</em></p>
<h3>Cherry on the cake 🍒🎂</h3>
<p>In our open source project <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>, we are using a small utils package across our applications and components called <code>deckdeckgo/utils</code> (published to <a href="https://www.npmjs.com/package/@deckdeckgo/utils" rel="noopener noreferrer external">npm</a>) which offers miscellaneous utilities. One of these being the vanilla Javascript <code>debounce</code> function. Therefore, if you need a quick and dirty solution, be our guest and give it a try 🖖</p>
<p>👉 <a href="https://github.com/deckgo/deckdeckgo/tree/master/webcomponents/utils" rel="noopener noreferrer external">https://github.com/deckgo/deckdeckgo/tree/master/webcomponents/utils</a></p>
<p>To infinity and beyond 🚀</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[We need your help to get started]]></title>
          <description><![CDATA[We need your help for the launch of DeckDeckGo our upcoming web open source editor for presentations]]></description>
          <link>https://daviddalbusco.com/blog/we-need-your-help-to-get-started/</link>
          <pubDate>Wed, 28 Aug 2019 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*rKupQcNGK0DIGsSwjPxVsQ.jpeg" alt="" / loading="lazy"><br />
<em>Photo by <a href="https://unsplash.com/@jonnysplsh?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Jonny Caspari</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I'm currently sitting in a bus between Zürich and Munich on my way for a weekend trip where I plan to not work and nerd that much. Ok I admit it, that plan doesn't begin exactly as I just told and I may have to work for a client tomorrow morning, but, I've got an awesome news to share:</p>
<p>We are about to launch <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>, our upcoming <a href="https://github.com/deckgo/deckdeckgo" rel="noopener noreferrer external">open source</a> web editor for presentations, next month (Mid to late September 2019) 🎉</p>
<p><img src="https://daviddalbusco.com/assets/images/1*H0grMl4nUD8LuzAl3QX8Dg.gif" alt="" / loading="lazy"></p>
<p><em>A quick overview of some of the features of our upcoming editor</em></p>
<h1>Launch without content sucks</h1>
<p><img src="https://daviddalbusco.com/assets/images/1*JMCskumAuEW6DcSDl02Z2w.gif" alt="" / loading="lazy"></p>
<p><em>The feels when they left the house, miss you Uncle Phil</em></p>
<p>It turns out that our solution is not &quot;just&quot; an editor but we are hopping that it would become someday an online community for sharing presentations, slides and talks about your interests and ideas (🤞). That's kind of our dream.</p>
<p><em>Oopsie I just felt asleep the last 30 minutes even if I'm listening to the loudest Australian's punk rock band in my headphones (<a href="https://www.youtube.com/watch?v=RN9NC4iQcsA" rel="noopener noreferrer external">Violent Soho</a>). I guess I could thank the EU for not being what it used to be anymore and for having gently walked me up to control my ID</em></p>
<p>We are far, far away of such an application and community but it does not mean that we can't dream a bit and that we could not implement the first piece of this idea in our beta, what we actually did. Its main screen, which I call the &quot;feed&quot;, will present all the decks which are going to be edited and published (as standalone Progressive Web Apps…but that's another story 😉) with our tool.</p>
<p>And there is our problem: we would not like to have a cold start respectively to launch our platform without content. Of course we are going to write by our own one or two presentations before its starts but it feels still very tiny in term of content. To summarize my feelings and speak frankly, that would sucks a bit.</p>
<p>That's why <strong>we need your help to get started</strong> and are hoping that you would be interested to give us a hand in order to create content before our official start 🙏</p>
<h1>Almost nothing to win</h1>
<p><img src="https://daviddalbusco.com/assets/images/1*nevdIt2eQwdLfv6fOlnFcA.gif" alt="" / loading="lazy"></p>
<p><em>What a strange choice of Gifs for this article, I miss Frank Drebin too</em></p>
<p>Whenever you have to create a new presentation in the next weeks, are just interested to try out our editor before anyone else or are maybe interested to replicate one of your favorite previous presentations, we would really appreciate your help.</p>
<p>You may ask yourself &quot;why David would I give you a hand if there is nothing to win?&quot;. Well that's fair point and I'm agree with you, we don't offer much, for not saying nothing in return but I could give you my word, that if one day we meet each other somewhere in the world, dinner or lunch will be on me!</p>
<p>Furthermore, if you would visit Zürich someday, I'll be happy to guide you through the city one afternoon. It ain't much, but I hope that this plus extra life's karma points will give you some motivation to participate 😇</p>
<h1>How to participate?</h1>
<p><img src="https://daviddalbusco.com/assets/images/1*5ay_HPHArSh83KMlHDhKnw.gif" alt="" / loading="lazy"></p>
<p><em>Join the dark side of the force - Gandalf</em></p>
<p>If you are still here and are up to give us hand, here's how you could participate and what's the plan:</p>
<ol>
<li><p>Reach me on <a href="https://twitter.com/daviddalbusco" rel="noopener noreferrer external">Twitter</a>, per <a href="mailto:david.dalbusco@outlook.com" rel="noopener noreferrer external">Email</a> or on our <a href="https://join.slack.com/t/deckdeckgo/shared_invite/enQtNzM0NjMwOTc3NTI0LTBlNmFhODNhYmRkMWUxZmU4ZTQ2MDJiNjlmYWZiODNjMDU5OGRjYThlZmZjMTc5YmQ3MzUzMDlhMzk0ZDgzMDY" rel="noopener noreferrer external">Slack channel</a> to get started</p></li>
<li><p>I'll answer and give you access to our platform. I will also be there in case you would need any help or support</p></li>
<li><p>You develop your presentations, ultimately publish them but don't share them &quot;publicly in the wild&quot; as long as we don't officially start</p></li>
<li><p>Hopefully, our tool isn't that buggy and you won't discover that much bugs, but you might find some, I mean, we are humans after all 🤖 If that would be the unfortunate case, obviously we are going to fix these before launch</p></li>
<li><p>Mid to late September 2019 we start our platform with plenty of content thanks to your help and we spend the all day wearing our biggest smiles because we are just lucky bastards who could count on awesome people like you 😃</p></li>
</ol>
<p><em>It’s the end of this article and I have still one hour and half of travel. I was about to develop the small <a href="https://github.com/deckgo/deckdeckgo/issues/234" rel="noopener noreferrer external">improvements</a> I just discovered before I began to write this article but I might just take a bit of time to read some pages of “<a href="https://www.amazon.com/Console-Wars-Nintendo-Defined-Generation-ebook/dp/B00FJ379XE/ref=pd_rhf_ee_p_img_1?_encoding=UTF8&amp;psc=1&amp;refRID=HCRGEEK0Q4E9V99715ZD" rel="noopener noreferrer external">Console Wars</a>” a recent book I bought about the story of Nintendo and Sega, will see.</em></p>
<p>To infinity and beyond 🚀</p>
<p>David</p>
<p><em>P.S. Funny story, it was actually only 30 remaining minutes, I totally misunderstand my ticket. It was a good surprise, kind of feel I've used a time travel machine</em> 🤣 <em>Cheers from Munich</em></p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Map a JSON file to ENUM in Java]]></title>
          <description><![CDATA[Map a JSON file to ENUM in Java]]></description>
          <link>https://daviddalbusco.com/blog/map-a-json-file-to-enum-in-java/</link>
          <pubDate>Fri, 16 Aug 2019 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*qSYqGVlUNNhJgsYDxenv9A.jpeg" alt="" / loading="lazy"><br />
<em>More about Le Gruyère AOP on <a href="https://www.myswitzerland.com/en-ch/experiences/food-wine/le-gruyere-aop/" rel="noopener noreferrer external">MySwitzerland.com</a></em></p>
<p>I recently began a new good old JAVA friend project for a returning client. One of my first task was to implement a new feature which had, notably, for goal to make the application globally configurable using a JSON property file.</p>
<p>I found the outcome of the solution relatively handy and therefore I thought that I would share it in a new blog post. Moreover, as I never wrote any Java blog post so far, I found it quite challenging and interesting 😉</p>
<h3>Introduction</h3>
<p>In this article we are going to:</p>
<ol>
<li>Create a new project</li>
<li>Read a JSON file and property</li>
<li>Create an ENUM</li>
<li>Map the properties to a generic ENUM</li>
</ol>
<p><em>Note: If you already have a project, you could obviously skip the first chapter which has for goal to create a project. Likewise, if you would not like to use Maven, skip it too and include the library we are going to use as requested by your setup.</em></p>
<h3>Create a new project</h3>
<p>To get started, we are going to create a new project using the <a href="https://maven.apache.org" rel="noopener noreferrer external">Maven</a> starter kit. For that purpose run the following command in a terminal:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">$ mvn archetype:generate </span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">DgroupId</span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2">com.jsontoenum.app </span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">DartifactId</span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2">json</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">to</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">enum </span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">DarchetypeArtifactId</span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2">maven</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">archetype</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">quickstart </span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">DarchetypeVersion</span><span style="color:#FF79C6">=</span><span style="color:#BD93F9">1.4</span><span style="color:#FF79C6"> -</span><span style="color:#F8F8F2">DinteractiveMode</span><span style="color:#FF79C6">=</span><span style="color:#BD93F9">false</span></span></code></pre><p>If everything went well, you should now be able to jump into the directory in order to compile the project without errors:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">$ cd json</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">to</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">enum</span><span style="color:#FF79C6">/</span><span style="color:#FF79C6"> &#x26;&#x26;</span><span style="color:#F8F8F2"> mvn </span><span style="color:#FF79C6">package</span></span></code></pre><h3>Read a JSON file and property</h3>
<p>At first I implemented a quick custom solution but I wasn’t, at all, enough happy with the outcome. That’s why I tried to find online a ready-to-go solution and found out the incredible open source library <a href="https://github.com/lightbend/config" rel="noopener noreferrer external">com.typesafe.config</a>. It leverages all the work to read JSON files and access their properties, has no dependencies and is even compatible with Java 8 🚀</p>
<p>For once at least, googling a solution was an excellent idea 😉</p>
<p><img src="https://daviddalbusco.com/assets/images/1*Q2m7fr30xL1yj3gNZIunNQ.jpeg" alt="" / loading="lazy"></p>
<p>To use the library, let’s add this library has a new dependency to our <code>pom.xml</code>:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">dependency</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">groupId</span><span style="color:#F8F8F2">>com.typesafe&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">groupId</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">artifactId</span><span style="color:#F8F8F2">>config&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">artifactId</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">version</span><span style="color:#F8F8F2">>1.3.4&#x3C;/</span><span style="color:#FF79C6">version</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">dependency</span><span style="color:#F8F8F2">></span></span></code></pre><p>Moreover we should also add the following <code>&lt;resources/&gt;</code> and <code>&lt;plugins/&gt;</code> goals to our <code>&lt;build/&gt;</code> in order to be able to load the JSON file (we are about to create) and to package the dependency we just referenced within our JAR file.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">resources</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;</span><span style="color:#FF79C6">resource</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">directory</span><span style="color:#F8F8F2">>src/resources&#x3C;/</span><span style="color:#FF79C6">directory</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;/</span><span style="color:#FF79C6">resource</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">resources</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">plugins</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;</span><span style="color:#FF79C6">plugin</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">groupId</span><span style="color:#F8F8F2">>org.apache.maven.plugins&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">groupId</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">artifactId</span><span style="color:#F8F8F2">>maven-shade-plugin&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">artifactId</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">version</span><span style="color:#F8F8F2">>3.2.0&#x3C;/</span><span style="color:#FF79C6">version</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">executions</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#FF79C6">execution</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">phase</span><span style="color:#F8F8F2">>package&#x3C;/</span><span style="color:#FF79C6">phase</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">goals</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">          &#x3C;</span><span style="color:#FF79C6">goal</span><span style="color:#F8F8F2">>shade&#x3C;/</span><span style="color:#FF79C6">goal</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;/</span><span style="color:#FF79C6">goals</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;/</span><span style="color:#FF79C6">execution</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#FF79C6">executions</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;/</span><span style="color:#FF79C6">plugin</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">plugins</span><span style="color:#F8F8F2">></span></span></code></pre><p>Our build is finally set up. We could now move on to the next step and create our JSON property file. Per default, the configuration library will try to load a file called <code>application.json</code> as source for the properties. Therefore, let’s keep it simple and use that predefined name to create this new file, in a new folder <code>src/resources/</code> , with the following JSON content:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#E9F284">  "</span><span style="color:#F1FA8C">swiss</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">: {</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">cheese</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">gruyere</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Everything is set, finally time to code 😇 We could modify our application (<code>App.java</code> in folder <code>src/main/java/com/jsontoenum/app)</code> in order to init and load the properties from the JSON file and print out the name of our favorite type of cheese 🧀</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">package</span><span style="color:#F8F8F2"> com.jsontoenum.app;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> com.typesafe.config.Config;</span></span>
<span class="line"><span style="color:#F8F8F2">import com.typesafe.config.ConfigFactory;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">public class App {</span></span>
<span class="line"><span style="color:#F8F8F2">    public static void main(String[] args) {</span></span>
<span class="line"><span style="color:#6272A4">        // Load and init the configuration library</span></span>
<span class="line"><span style="color:#F8F8F2">        final Config conf = ConfigFactory.load();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">        // Get the value of the JSON property</span></span>
<span class="line"><span style="color:#F8F8F2">        final String cheese = conf.getString(</span><span style="color:#F1FA8C">"swiss.cheese"</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        System.out.println(String.format(</span><span style="color:#F1FA8C">"I like %s 🧀"</span><span style="color:#F8F8F2">, cheese));</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Let’s compile everything and run our project using the following command line in a terminal to try out our implementation:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">$ mvn </span><span style="color:#FF79C6">package</span><span style="color:#FF79C6"> &#x26;&#x26;</span><span style="color:#F8F8F2"> java </span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">cp target</span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2">json</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">to</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">enum</span><span style="color:#FF79C6">-</span><span style="color:#BD93F9">1.0</span><span style="color:#FF79C6">-</span><span style="color:#BD93F9">SNAPSHOT</span><span style="color:#F8F8F2">.jar com.jsontoenum.app.App</span></span></code></pre><p>If everything went find, your console output should display the following at the end of the stacktrace:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">[INFO] </span><span style="color:#FF79C6">-------------------------------------------------------------</span></span>
<span class="line"><span style="color:#F8F8F2">[INFO] BUILD SUCCESS</span></span>
<span class="line"><span style="color:#F8F8F2">[INFO] </span><span style="color:#FF79C6">-------------------------------------------------------------</span></span>
<span class="line"><span style="color:#F8F8F2">[INFO] Total time:  </span><span style="color:#BD93F9">2.179</span><span style="color:#F8F8F2"> s</span></span>
<span class="line"><span style="color:#F8F8F2">[INFO] Finished at: </span><span style="color:#BD93F9">2019</span><span style="color:#FF79C6">-</span><span style="color:#BD93F9">08</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">16T15:</span><span style="color:#BD93F9">04</span><span style="color:#F8F8F2">:</span><span style="color:#BD93F9">35</span><span style="color:#FF79C6">+</span><span style="color:#BD93F9">02</span><span style="color:#F8F8F2">:</span><span style="color:#BD93F9">00</span></span>
<span class="line"><span style="color:#F8F8F2">[INFO] </span><span style="color:#FF79C6">-------------------------------------------------------------</span></span>
<span class="line"><span style="color:#F8F8F2">I like gruyere 🧀</span></span></code></pre><h3>Create an ENUM</h3>
<p>Even if it was already cool to be able to read JSON properties, I quickly realized, while developing my assigned feature, that being able to map the properties to ENUM would kind of be mandatory if the application is supposed to behave differently according these. Moreover, Switzerland produces more than one kind of cheese 😉</p>
<p>As next step we could therefore create an ENUM in folder <code>src/main/java/com/jsontoenum/app/</code> which could list a couple of cheese from the French-speaking part of the country:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">package</span><span style="color:#F8F8F2"> com.jsontoenum.app;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">public </span><span style="color:#FF79C6">enum</span><span style="color:#8BE9FD;font-style:italic"> Cheese</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">   GRUYERE,</span></span>
<span class="line"><span style="color:#F8F8F2">   TETE_DE_MOINE,</span></span>
<span class="line"><span style="color:#F8F8F2">   CHAUX_DABEL,</span></span>
<span class="line"><span style="color:#F8F8F2">   RACLETTE,</span></span>
<span class="line"><span style="color:#F8F8F2">   VACHERIN,</span></span>
<span class="line"><span style="color:#F8F8F2">   TOMME</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><h3>Map the properties to a generic ENUM</h3>
<p>In this article we are using cheese for demo purpose but obviously, Switzerland exports many other products like chocolates or watches. Likewise, the application I’m working on doesn’t contains only a single ENUM. That’s why we are not just going to add a method to parse the properties to a single type of ENUM but rather declare it as generic in our application (<code>App.java</code> ):</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">private static &#x3C;</span><span style="color:#FFB86C;font-style:italic">E</span><span style="color:#FF79C6"> extends</span><span style="color:#FFB86C;font-style:italic"> Enum</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">E</span><span style="color:#F8F8F2">>> E getEnumProperty(</span><span style="color:#FFB86C;font-style:italic">final</span><span style="color:#FFB86C;font-style:italic"> Config</span><span style="color:#FFB86C;font-style:italic"> conf</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">final</span><span style="color:#FFB86C;font-style:italic"> String</span><span style="color:#FFB86C;font-style:italic"> key</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">final</span><span style="color:#FFB86C;font-style:italic"> Class</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">E</span><span style="color:#F8F8F2">> </span><span style="color:#FFB86C;font-style:italic">myClass</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#6272A4">    // If no config loaded</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (conf </span><span style="color:#FF79C6">==</span><span style="color:#BD93F9"> null</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#BD93F9"> null</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // If the config doesn't contains the key</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">conf.</span><span style="color:#50FA7B">hasPath</span><span style="color:#F8F8F2">(key)) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#BD93F9"> null</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // As previously, load the key value</span></span>
<span class="line"><span style="color:#F8F8F2">    final String keyValue </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> conf.</span><span style="color:#50FA7B">getString</span><span style="color:#F8F8F2">(key);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // Does the property has a value</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (keyValue </span><span style="color:#FF79C6">==</span><span style="color:#BD93F9"> null</span><span style="color:#FF79C6"> ||</span><span style="color:#F8F8F2"> keyValue.</span><span style="color:#50FA7B">isEmpty</span><span style="color:#F8F8F2">()) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#BD93F9"> null</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // Map the property to the ENUM</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> Enum.</span><span style="color:#50FA7B">valueOf</span><span style="color:#F8F8F2">(myClass, keyValue.</span><span style="color:#50FA7B">toUpperCase</span><span style="color:#F8F8F2">());</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Finally, for the final test, let’s enhance our <code>main</code> method by loading the ENUM and testing it to display if the cheese is our favorite one or not:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">public static </span><span style="color:#FF79C6">void</span><span style="color:#50FA7B"> main</span><span style="color:#F8F8F2">(String[] args) {</span></span>
<span class="line"><span style="color:#6272A4">    // Load and init the configuration library</span></span>
<span class="line"><span style="color:#F8F8F2">   final Config conf </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> ConfigFactory.</span><span style="color:#50FA7B">load</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">   // Get the value of the JSON property</span></span>
<span class="line"><span style="color:#F8F8F2">   final Cheese cheese </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#50FA7B">                getEnumProperty</span><span style="color:#F8F8F2">(conf, </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">swiss.cheese</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, Cheese.class);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">   if</span><span style="color:#F8F8F2"> (Cheese.</span><span style="color:#BD93F9">GRUYERE</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">equals</span><span style="color:#F8F8F2">(cheese)) {</span></span>
<span class="line"><span style="color:#F8F8F2">       System.out.</span><span style="color:#50FA7B">println</span><span style="color:#F8F8F2">(String.</span><span style="color:#50FA7B">format</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">I really like %s 🧀</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, cheese));</span></span>
<span class="line"><span style="color:#F8F8F2">   } </span><span style="color:#FF79C6">else</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">       System.out.</span><span style="color:#50FA7B">println</span><span style="color:#F8F8F2">(String.</span><span style="color:#50FA7B">format</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">%s is ok</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, cheese));</span></span>
<span class="line"><span style="color:#F8F8F2">   }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Voilà, that’s it, nothing more, nothing less 🎉</p>
<p>You could try to run our project using the previous command line and if everything goes according plan, the output should looks like the following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">[INFO] </span><span style="color:#FF79C6">-------------------------------------------------------------</span></span>
<span class="line"><span style="color:#F8F8F2">[INFO] BUILD SUCCESS</span></span>
<span class="line"><span style="color:#F8F8F2">[INFO] </span><span style="color:#FF79C6">-------------------------------------------------------------</span></span>
<span class="line"><span style="color:#F8F8F2">[INFO] Total time:  </span><span style="color:#BD93F9">2.437</span><span style="color:#F8F8F2"> s</span></span>
<span class="line"><span style="color:#F8F8F2">[INFO] Finished at: </span><span style="color:#BD93F9">2019</span><span style="color:#FF79C6">-</span><span style="color:#BD93F9">08</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">16T15:</span><span style="color:#BD93F9">43</span><span style="color:#F8F8F2">:</span><span style="color:#BD93F9">42</span><span style="color:#FF79C6">+</span><span style="color:#BD93F9">02</span><span style="color:#F8F8F2">:</span><span style="color:#BD93F9">00</span></span>
<span class="line"><span style="color:#F8F8F2">[INFO] </span><span style="color:#FF79C6">-------------------------------------------------------------</span></span>
<span class="line"><span style="color:#F8F8F2">I really like GRUYERE 🧀</span></span></code></pre><h3>Cherry on the cake 🍒🎂</h3>
<p>If you want to spare the hassle of creating your own project and copying the above code, I have published the project online, be my guest 😇</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">$ git clone https:</span><span style="color:#6272A4">//github.com/peterpeterparker/json-to-enum.git</span></span></code></pre><p>To infinity and beyond 🚀</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Outcome of our first call for contributors]]></title>
          <description><![CDATA[Outcome of our first call for contributors]]></description>
          <link>https://daviddalbusco.com/blog/outcome-of-our-first-call-for-contributors/</link>
          <pubDate>Thu, 01 Aug 2019 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*tQGx3mWFA0Oq3rGLA4Ypgg.gif" alt="" / loading="lazy"></p>
<p><em>Wow — Owen Wilson</em></p>
<p>Last week we published our <a href="https://daviddalbusco.com/blog/contribute-to-our-open-source-project" rel="noopener noreferrer external">first call</a> for contributors for our open source project <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a> and the outcomes are just mesmerizing 🤩 Therefore, before going further, we would like first to thanks each and every persons who read the article, interacted with or volunteered to resolve issues and even sent us Pull Requests. You are truly awesome and your reactions cheered up all up, <strong>THANK YOU</strong> ❤️❤️❤️</p>
<h3>Pull requests</h3>
<p>Following our blog post, many persons contacted us to take over the issues we listed and even found other issues to solve in our repo 😜 Some of these are still in progress but two notable features have now been developed, that’s why we thought that it would be cool to display their outcomes and to give credits to their authors.</p>
<h3>Before we start</h3>
<p>Something I personally really like in our project is its modularity. All our web components could be use as standalone components in any applications (“with or without any modern frameworks”), or could be use in our starter kit if you are developing your presentation using HTML or Markdown (<code>npm init deckdeckgo</code> ) and are also used in our upcoming web editor for presentations.</p>
<p>It’s kind of cool to think, that if you are improving or fixing a component, this improvement could be use in so many constructs.</p>
<h3>Optional line numbers for code highlighting</h3>
<p>The goal of this feature was to add an option to our code highlighter component, which use <a href="https://prismjs.com" rel="noopener noreferrer external">Prism.js</a> under the hood, in order to be able to automatically display line numbers at the begin of each lines.</p>
<p>This feature was developed by Stefan Nieuwenhuis (<a href="https://twitter.com/stefannhs" rel="noopener noreferrer external">Twitter</a> / <a href="https://github.com/StefanNieuwenhuis" rel="noopener noreferrer external">GitHub</a>) and was submitted in the PR <a href="https://github.com/deckgo/deckdeckgo/pull/213" rel="noopener noreferrer external">#213</a>. Not all heroes wear capes, you are awesome Stefan 👍</p>
<p><img src="https://daviddalbusco.com/assets/images/1*nuVLuov6bXRr6SpREDzzxA.gif" alt="" / loading="lazy"></p>
<p><em>Automatically display line numbers at the begin of the code you want to showcase</em></p>
<h3>Youtube short Url</h3>
<p>We faced an issue with our component and “Youtube” template (which allow users and developers to easily integrate Youtube video in their presentations) when short url were used to embed video, as urls have to respect a specific format.</p>
<p>This fix was developed by Rohit Bokade (<a href="https://github.com/bokaderohit98" rel="noopener noreferrer external">GitHub</a>) and was submitted in PR <a href="https://github.com/deckgo/deckdeckgo/pull/207" rel="noopener noreferrer external">#207</a>. The force is strong with you Rohit, thanks a lot 👍</p>
<p><img src="https://daviddalbusco.com/assets/images/1*GpJzea6l2X946tufjgb5kw.gif" alt="" / loading="lazy"></p>
<p><em>Integrate easily a YouTube video to your presentation</em></p>
<h3>Cherry on the cake 🍒🎂</h3>
<p>We still have <a href="https://github.com/deckgo/deckdeckgo/issues" rel="noopener noreferrer external">issues</a> to solve 🤣 Most of them flagged as “good first issue” are in progress and the others might not be that straight forward, but who knows, you might find one you are interested in and more important, we are really open to any idea or suggestions. Therefore don’t hesitate to submit your feature requests, that would be super cool.</p>
<p>To infinity and beyond 🚀</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Contribute to our open source project]]></title>
          <description><![CDATA[Are you looking to contribute to a new open source project? Or want to get started with technologies like Web Components and StencilJS ? Contribute to our open source project DeckDeckGo]]></description>
          <link>https://daviddalbusco.com/blog/contribute-to-our-open-source-project/</link>
          <pubDate>Fri, 26 Jul 2019 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*5Fjoye88JFo3hqnenly5UQ.jpeg" alt="" / loading="lazy"><br />
<em>Photo by <a href="https://unsplash.com/@alvaroserrano?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Álvaro Serrano</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>Are you looking to contribute to a new open source project? Or want to get started with technologies like Web Components and <a href="https://stenciljs.com" rel="noopener noreferrer external">StencilJS</a> ?</p>
<p>We are developing <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>, a web open source editor for presentations, and would love to count on you as a contributor to improve our components and applications 🙏</p>
<h3>Get started</h3>
<p>As the project’s size grew quite a bit the past couple of months, you might ask yourself, where to get started 🤔?</p>
<p>Well, guess what, I asked my self the same question 🤣 After thinking about it, I came to the conclusion that the process will be iterative for both of us. Moreover than helping us resolve issues, you will help us learn how to better structure issues for contributors and also help us shape the process.</p>
<p>For the purpose of this first experience, I have then selected the following four features which have to be implemented in our “core” and “highlight-code” Web Components developed with StencilJS.</p>
<h3>The countdown template</h3>
<p>The presentations developed with DeckDeckGo are based on predefined templates. These could be simple text templates, where the text boxes are for example centered in the slides, or where the slides are split in two columns or even a bit funnier where Gifs could be easily integrated.</p>
<p>A couple of months ago, I went to a meetup where the organizer kept telling “we are beginning in 10 minutes” followed by “we are beginning in 5 minutes” and then in “now in 2 minutes” etc. I thought, specially if there is a large crowd, that it would be maybe cool to just display the information on the big screen. That’s why I came to the idea of having a template where the user could just pass a date and time as parameter and DeckDeckGo takes care or rendering a count down on screen 😁</p>
<p><img src="https://daviddalbusco.com/assets/images/1*voVxknfmRH_le-JkDvJD2Q.png" alt="" / loading="lazy"></p>
<p><em>Our remote control has a “timer” feature, maybe its design could be use as inspiration</em></p>
<p>Interested? 👉 <a href="https://github.com/deckgo/deckdeckgo/issues/45" rel="noopener noreferrer external">Issue #45: [core] the countdown slide template</a></p>
<h3>Transition “face effect”</h3>
<p>The “only” transition effect currently happening when swiping between slides is a “slide” effect (current slide is dragged away of the window and the new one is dragged in).</p>
<p><img src="https://daviddalbusco.com/assets/images/1*kEdSJGj91MbKjl3as1w7FA.gif" alt="" / loading="lazy"></p>
<p><em>Current “slide” effect</em></p>
<p>I thought it would be neat to have an option to select another type of animation beginning with the implementation of a “fade effect” transition.</p>
<p>Interested? 👉 <a href="https://github.com/deckgo/deckdeckgo/issues/70" rel="noopener noreferrer external">Issue #70: [core] Transition animation with “fade effect”</a></p>
<h3>Youtube short Url</h3>
<p>Among all the templates, there is a <a href="https://docs.deckdeckgo.com/slides/youtube" rel="noopener noreferrer external">YouTube template</a> which helps to easily integrate YouTube videos in the presentations. It is also interconnected with our <a href="https://deckdeckgo.app" rel="noopener noreferrer external">remote control</a> to let the speakers stop/start their video remotely 📱</p>
<iframe width="280" height="158" src="https://www.youtube.com/embed/3o3oGBTTRSs" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe
<br/>
<p><em>A video I recorded when I released the remote control</em></p>
<p>In order to be able to embed a YouTube video, its Url has to follow a specific format which isn’t the same as the one of the short Url exported by the social share component used by Youtube. That’s why, if such Url would be detected, the id of the video would have to be extracted in order to be formatted in a correct link.</p>
<p>We already have implemented this for Url which would be copied/pasted from the browser navigation bar, therefore this improvement would only be an “improvement” to the existing.</p>
<p>Interested? 👉 <a href="https://github.com/deckgo/deckdeckgo/issues/204" rel="noopener noreferrer external">Issue #204: [core] Handle Youtube short URL</a></p>
<h3>Optional line numbers for code highlighting</h3>
<p>With our Web Component to format and highlight code, which use <a href="https://prismjs.com" rel="noopener noreferrer external">Prism.js</a>under the hood, it is already possible to highlight specific lines. I was thinking that adding an option to add automatically line numbers at the begin of each lines would be a nice add-ons. I could think that these numbers could be useful when the speaker want to reference a line while talking or also when the component would be use in a web page, might help if needed to make references more clear.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*fv8n52dHip5HUJABheKN2w.png" alt="" / loading="lazy"></p>
<p><em>A sneak peek of this component in our upcoming editor for presentations</em></p>
<p>Interested? 👉 <a href="https://github.com/deckgo/deckdeckgo/issues/115" rel="noopener noreferrer external">Issue #115: [highlight-code] display optionally line numbers</a></p>
<h3>Cherry on the cake 🍒🎂</h3>
<p>Beside unlocking some karma points, you know what would be cool with your contribution? All the improvements you will submit will not have effect to the components, they will have an impact and be included in our upcoming editor and platform for presentations. Kind of cool to know that something you would develop will for sure be available in an online live application, isn’t it?</p>
<p>To infinity and beyond!</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[How to take a screenshot of a website in the cloud]]></title>
          <description><![CDATA[How to take a screenshot of a website in the cloud using Puppeteer in a Google Firebase Functions and to save the results to Google Firebase Storage]]></description>
          <link>https://daviddalbusco.com/blog/how-to-take-a-screenshot-of-a-website-in-the-cloud/</link>
          <pubDate>Sat, 20 Jul 2019 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://thepracticaldev.s3.amazonaws.com/i/nsa5dduag5csoamhu28o.jpg" alt="" / loading="lazy"></p>
<p><em>Photo by <a href="https://unsplash.com/@samsomfotos?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">samsommer</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>We are currently <a href="https://daviddalbusco.com/blog/we-are-developing-an-open-source-editor-for-presentations" rel="noopener noreferrer external">developing</a> an open source web editor for presentations called <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a> which, hopefully, we will be able to release in beta around the end of the summer (🤞).</p>
<p>Why I allowed my self to be so frontal in my opening sentence? Well, here we go: I'm happy to officially announce that it will not &quot;just&quot; be an editor but that we also designed it to be an online platform where presentations will be shared and ultimately, if I let my self dream a bit about the future, will be indexed according your interests and maybe will be even discussed.</p>
<p>We are far, far away of such an application and community but it does not mean we can't dream a bit, specially as the project is a side one which we implement at nights and on the weekends for fun. Therefore we have decided to already implement the first piece of this idea in our beta. The main screen, which I call the &quot;feed&quot;, will present all the decks which are going to be edited and published (as standalone Progressive Web Apps…but that's another story 😉) with our tool. For that purpose, as for their social cards, we had the need, or we decided, to capture in the cloud a screenshot of the first slide of such decks and that's why I'm writing this new blog post to display how you could create a <a href="https://firebase.google.com/docs/functions" rel="noopener noreferrer external">Google Functions for Firebase</a> to take a screenshot of a website in the cloud 📸</p>
<p><img src="https://thepracticaldev.s3.amazonaws.com/i/o9122u7ms0zku9xcevo1.gif" alt="" / loading="lazy"></p>
<p><em>A sneak peek of our feed filled with our test presentations</em></p>
<h1>Introduction of the solution</h1>
<p>As introduced above, for this solution, we are going to write a new Google Functions for Firebase in order to run our process in the cloud. For the particular case of the screenshot, we are going to use <a href="https://github.com/GoogleChrome/puppeteer" rel="noopener noreferrer external">Puppeteer</a>. We are going to use it to run a headless Chrome, where we are going to load the website, and to take the screenshot too. Finally we are going to write the results in <a href="https://firebase.google.com/docs/storage" rel="noopener noreferrer external">Google Firebase Storage</a>.</p>
<h1>Before we start</h1>
<p>To be honest, as always, Google published a really well written article in August 2018 which introduces the <a href="https://cloud.google.com/blog/products/gcp/introducing-headless-chrome-support-in-cloud-functions-and-app-engine" rel="noopener noreferrer external">headless Chrome support in Cloud Functions and App Engine</a> which exactly displays how to take a screenshot. In comparison to this article, this post introduces the storage but also improves a bit the way of capturing the screenshot, so I hope you still do think it deserves a read and even your time 🙏</p>
<p><em>In this &quot;tutorial&quot; I am going to skip the part where you setup and interact with your <a href="https://firebase.google.com/docs/functions" rel="noopener noreferrer external">Google Firebase Functions</a> or Storage, there are dozen of documentations and tutorials about it and, furthermore, the Google Firebase Console even provides wizards to drive you through all these process. You could also note that I use <a href="https://firebase.google.com/docs/functions/typescript" rel="noopener noreferrer external">TypeScript</a> to write the functions and we are going to write all our code in the main file</em> <code>src/index.ts</code>.</p>
<h1>Getting started</h1>
<p>For the purpose of this article we are going to create an <a href="https://firebase.google.com/docs/functions/http-events" rel="noopener noreferrer external">HTTP functions</a> which could be, obviously, invoked through HTTP. In the particular case of DeckDeckGo, we are using a <a href="https://firebase.google.com/docs/functions/database-events" rel="noopener noreferrer external">realtime database triggers</a> to trigger the process. Therefore, just in case you would ask yourself, yes, regardless of the trigger, such a solution could be implemented.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#BD93F9"> *</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> functions </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">firebase-functions</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> takeScreenshot </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#F8F8F2">             functions.https.</span><span style="color:#50FA7B">onRequest</span><span style="color:#F8F8F2">(takeScreenShotOnRequest);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> takeScreenShotOnRequest</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">request</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">response</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#6272A4">    // 1. Generate screenshot</span></span>
<span class="line"><span style="color:#6272A4">    // 2. Save to the storage</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><h1>Take the screenshot</h1>
<p>As explained above, we are going to use Puppeteer to capture the screenshot, therefore, first things first, we have to install the dependency and its type definition for typescript:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">$ npm install puppeteer </span><span style="color:#FF79C6">--</span><span style="color:#F8F8F2">save</span></span>
<span class="line"><span style="color:#F8F8F2">$ npm install @</span><span style="color:#50FA7B;font-style:italic">types</span><span style="color:#FF79C6">/</span><span style="color:#50FA7B;font-style:italic">puppeteer</span><span style="color:#FF79C6"> --</span><span style="color:#F8F8F2">save</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">dev</span></span></code></pre><p>Once installed, we could now modify our previous code and implement our function:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#BD93F9"> *</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> functions </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">firebase-functions</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#BD93F9"> *</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> puppeteer </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">puppeteer</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> takeScreenshot </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#F8F8F2">             functions.https.</span><span style="color:#50FA7B">onRequest</span><span style="color:#F8F8F2">(takeScreenShotOnRequest);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> takeScreenShotOnRequest</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">request</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">response</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">    try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> imageBuffer</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> generateScreenShot</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">        // 2. Save to the storage</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#F8F8F2">        console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(err);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">function</span><span style="color:#50FA7B"> generateScreenShot</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">>(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">reject</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">            const</span><span style="color:#F8F8F2"> browser </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#FF79C6">                  await</span><span style="color:#F8F8F2"> puppeteer.</span><span style="color:#50FA7B">launch</span><span style="color:#F8F8F2">({args</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">--no-sandbox</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">]});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">            const</span><span style="color:#F8F8F2"> page </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> browser.</span><span style="color:#50FA7B">newPage</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">            // Screenshot size</span></span>
<span class="line"><span style="color:#FF79C6">            await</span><span style="color:#F8F8F2"> page.</span><span style="color:#50FA7B">setViewport</span><span style="color:#F8F8F2">({width</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 1024</span><span style="color:#F8F8F2">, height</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 576</span><span style="color:#F8F8F2">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">            // Go to your website</span></span>
<span class="line"><span style="color:#FF79C6">            await</span><span style="color:#F8F8F2"> page.</span><span style="color:#50FA7B">goto</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">https://deckdeckgo.com</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">            // Disable service workers</span></span>
<span class="line"><span style="color:#FF79C6">            await</span><span style="color:#F8F8F2"> (page </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> any</span><span style="color:#F8F8F2">)._client</span></span>
<span class="line"><span style="color:#F8F8F2">                  .</span><span style="color:#50FA7B">send</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">ServiceWorker.enable</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">            await</span><span style="color:#F8F8F2"> (page </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> any</span><span style="color:#F8F8F2">)._client</span></span>
<span class="line"><span style="color:#F8F8F2">                  .</span><span style="color:#50FA7B">send</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">ServiceWorker.stopAllWorkers</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">            // Wait for a particular components to be loaded</span></span>
<span class="line"><span style="color:#FF79C6">            await</span><span style="color:#F8F8F2"> page</span></span>
<span class="line"><span style="color:#F8F8F2">                  .</span><span style="color:#50FA7B">waitForFunction</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">document.querySelector("deckgo-deck  > *")</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">            // Take the screenshot</span></span>
<span class="line"><span style="color:#FF79C6">            const</span><span style="color:#F8F8F2"> imageBuffer</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> page.</span><span style="color:#50FA7B">screenshot</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">            await</span><span style="color:#F8F8F2"> browser.</span><span style="color:#50FA7B">close</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">            resolve</span><span style="color:#F8F8F2">(imageBuffer);</span></span>
<span class="line"><span style="color:#F8F8F2">        } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#50FA7B">            reject</span><span style="color:#F8F8F2">(err);</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>What's happening there 🤔? We are telling Puppeteer to run a headless Chrome where we access our website in a page with a specific size which ultimately will be the size (width and height) of our screenshot. Moreover, we are disabling the service workers (&quot;no cache = always fresh data&quot;) and are also waiting for a particular element to be loaded before taking the screenshot. If your goal is to take a screenshot from a static website, of course you could step that particular step. Finally, we are telling Puppeteer to take a screenshot and we are returning the image buffer as result of the function.</p>
<h2>Special credits</h2>
<p>I did not came to that really neat solution alone. It is actually the result of an exchange with Matthias Max, CEO of <a href="https://www.bitflower.net/" rel="noopener noreferrer external">bitflower</a>, on the <a href="https://stencil-worldwide.herokuapp.com/" rel="noopener noreferrer external">StencilJS Slack</a> channel. Kudos to him, I would for example not have seriously thought in a first place on disabling the service workers if he would not have shared his idea and code, thanks a lot Matthias 👍</p>
<h2>Tips and tricks</h2>
<p>In case you would need more memory to run your cloud function (it was the case for our project), you could extend the above declaration with, for example, 1GB of memory and a timeout of 2 minutes.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> runtimeOpts </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    timeoutSeconds</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 120</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    memory</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FF79C6">const</span><span style="color:#F8F8F2">> '1GB'</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">export const takeScreenshot =</span></span>
<span class="line"><span style="color:#F8F8F2">                 functions.runWith(runtimeOpts)</span></span>
<span class="line"><span style="color:#F8F8F2">                 .https.onRequest(takeScreenShotOnRequest);</span></span></code></pre><h1>Save to the storage</h1>
<p>Saving the image buffer to the storage is actually, don't know why I'm still surprised by the simplicity of Google Firebase, really easy. Using the Firebase Admin we just need to reference the default bucket, create a file object and saves it, nothing more, nothing left.</p>
<p>Nota bene: no need to check or create specific folders and subfolders, the library handles everything for you. Also if you would run the process multiple times, per default, the resulting file in the storage are just going to be overwritten.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#BD93F9"> *</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> functions </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">firebase-functions</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#BD93F9"> *</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> puppeteer </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">puppeteer</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#BD93F9"> *</span><span style="color:#FF79C6"> as</span><span style="color:#F8F8F2"> admin </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">firebase-admin</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> takeScreenshot </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#F8F8F2">             functions.https.</span><span style="color:#50FA7B">onRequest</span><span style="color:#F8F8F2">(takeScreenShotOnRequest);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">async</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> takeScreenShotOnRequest</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">request</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">response</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">    try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> imageBuffer</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> generateScreenShot</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">        await</span><span style="color:#50FA7B"> saveScreenShot</span><span style="color:#F8F8F2">(imageBuffer);</span></span>
<span class="line"><span style="color:#F8F8F2">    } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#F8F8F2">        console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(err);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"><span style="color:#FF79C6">function</span><span style="color:#50FA7B"> generateScreenShot</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">>(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">reject</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#6272A4">            // Replace with the same code as in previous chapter</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">            resolve</span><span style="color:#F8F8F2">(imageBuffer);</span></span>
<span class="line"><span style="color:#F8F8F2">        } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#50FA7B">            reject</span><span style="color:#F8F8F2">(err);</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">function</span><span style="color:#50FA7B"> saveScreenShot</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">imageBuffer</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">>(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">reject</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">imageBuffer </span><span style="color:#FF79C6">||</span><span style="color:#F8F8F2"> imageBuffer </span><span style="color:#FF79C6">===</span><span style="color:#E9F284"> ''</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#50FA7B">            reject</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">No screenshot</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#6272A4">            // We get the instance of our default bucket</span></span>
<span class="line"><span style="color:#FF79C6">            const</span><span style="color:#F8F8F2"> bucket </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> admin.</span><span style="color:#50FA7B">storage</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">bucket</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">            // Create a file object</span></span>
<span class="line"><span style="color:#FF79C6">            const</span><span style="color:#F8F8F2"> file </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> bucket.</span><span style="color:#50FA7B">file</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`/screenshots/deckdeckgo.png`</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">            // Save the image</span></span>
<span class="line"><span style="color:#FF79C6">            await</span><span style="color:#F8F8F2"> file.</span><span style="color:#50FA7B">save</span><span style="color:#F8F8F2">(imageBuffer);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">            resolve</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">        } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#50FA7B">            reject</span><span style="color:#F8F8F2">(err);</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>That's it, we have implemented a Google Cloud Function to take and save a screenshot of our website 🎉</p>
<h1>Trying it out</h1>
<p>As I said above, I won't go in the details of the interaction with Firebase. That being said, you could deploy your function running the following command in a terminal:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">$ firebase deploy</span></span></code></pre><p>And you could also try it out running the following curl command in your terminal too:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">$ curl </span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">i </span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">H </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Accept: application/json</span><span style="color:#E9F284">"</span><span style="color:#FF79C6"> -</span><span style="color:#F8F8F2">X GET  https:</span><span style="color:#6272A4">//us-central1-your-cool-app.cloudfunctions.net/takeScreenshot</span></span></code></pre><p><em>Replace <code>https://us-central1-your-cool-app.cloundfuntions.net</code> with your application/functions URL.</em></p>
<h1>Cherry on the cake 🍒🎂</h1>
<p>Our project is open source and we try to encourage others to do so too, that's why we released this particular function in our monorepo under the <a href="https://en.wikipedia.org/wiki/GNU_Affero_General_Public_License" rel="noopener noreferrer external">GNU Affero General Public License</a>. Therefore if your project is cool too (😉) be our guest and clone:</p>
<p><code>https://github.com/deckgo/deckdeckgo/tree/master/cloud</code>.</p>
<p>To infinity and beyond 🚀<br />
David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Take photo and access the picture library in your PWA (without plugins)]]></title>
          <description><![CDATA[How to access camera and photo library in a Progressive Web App using web technologies and no plugins]]></description>
          <link>https://daviddalbusco.com/blog/take-photo-and-access-the-picture-library-in-your-pwa-without-plugins/</link>
          <pubDate>Wed, 10 Jul 2019 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*94yTSEwDkPK52B22ll74mQ.jpeg" alt="" / loading="lazy"><br />
<em>When I wrote this post I was visiting Frankfurt, I could had use your PWA to take this picture 😉</em></p>
<p>I recently developed a new feature in our upcoming web open source editor for presentations, <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>, to let users upload their own pictures. Turn out, as our application is a Progressive Web App, that the feature I implemented not only let users access their picture library on their phones but also allow them to take photos and upload these directly in their presentations 🚀</p>
<p>In this new blog post I’ll share how you could implement such a feature and will also try to display how you could limit the uploaded file size, upload the results to <a href="https://firebase.google.com/docs/storage" rel="noopener noreferrer external">Firebase Storage</a> (the solution I implemented) or convert the result to a <a href="https://en.wikipedia.org/wiki/Base64" rel="noopener noreferrer external">base64</a> string or to a blob with a local object URL (in case you would have one of these requirements).</p>
<iframe width="280" height="158" src="https://www.youtube.com/embed/VMNa3RnWxHI" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe
<br/>
<p><em>The outcome of this blog post on an Android phone</em></p>
<h3>Before we start</h3>
<p>The following solution works just fine in a Progressive Web App but are not going to work in an iOS and Android application you would develop with web technologies. Therefore, if you are targeting the web, both above platforms and only want to write one single code base for all of these, I could advise you to have a look, for example, to <a href="https://capacitor.ionicframework.com" rel="noopener noreferrer external">Capacitor</a>.</p>
<h3>Getting started</h3>
<p>In order to be able to access the camera and the photo library on mobile devices with the Web we actually only need an <code>&lt;input/&gt;</code> element with <code>type=&quot;file&quot;</code> , nothing more nothing left. It’s nowadays well supported on both iOS and Android. On desktop, same element will give access to a file(s) picker instead.</p>
<p>Furthermore, as we may want to “only” import certain types of images, we may restrict the file types the file input should accept using the, well I guess you get it, <code>accept</code> attribute.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">input</span><span style="color:#50FA7B;font-style:italic"> type</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">file</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> accept</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">image/x-png,image/jpeg,image/gif</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">/></span></span></code></pre><h3>Accessing the image</h3>
<p>No matter if camera, photo library or file picker, once the user has perform the action, the resulting image is available as an attribute <code>files</code> of the <code>input</code> but we still need an event to trigger the next process. For that purpose we are going to hook on the <code>onChange</code> event of the <code>input</code> which is triggered as soon as the action has been completed.</p>
<p><em>Note: I wrote and display the solution using JSX as I developed mine with <a href="https://stenciljs.com" rel="noopener noreferrer external">StencilJS</a>. You could adapt it easily to Javascript. If you are facing trouble doing so, ping me, I’ll be happy to assist.</em></p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">input</span><span style="color:#50FA7B;font-style:italic"> type</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">file</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> accept</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">image/x-png,image/jpeg,image/gif</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                       onChange</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">upload</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">/></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">private </span><span style="color:#50FA7B">upload</span><span style="color:#F8F8F2">(): </span><span style="color:#8BE9FD;font-style:italic">Promise</span><span style="color:#FF79C6">&#x3C;void></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    return new </span><span style="color:#50FA7B">Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">void</span><span style="color:#F8F8F2">>(</span><span style="color:#FFB86C;font-style:italic">async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">reject</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">        const</span><span style="color:#F8F8F2"> filePicker </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> document.</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">input</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#50FA7B">        if</span><span style="color:#F8F8F2"> (!</span><span style="color:#FFB86C;font-style:italic">filePicker</span><span style="color:#F8F8F2"> || !filePicker.files</span></span>
<span class="line"><span style="color:#F8F8F2">            || filePicker.files.length &#x3C;</span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#50FA7B">            reject</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">No file selected.</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">        const</span><span style="color:#F8F8F2"> myFile </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> filePicker.files[</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">        console</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">log</span><span style="color:#F8F8F2">(</span><span style="color:#8BE9FD;font-style:italic">myFile</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#50FA7B">        resolve</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>An <code>input</code> of type <code>file</code> could be use to select multiple files too but in this post we are only considering selecting one as displayed above when assigning the result to a local variable <code>myFile</code>.</p>
<p>That’s it for the GUI implementation, nothing more, nothing less, the web is awesome 😄</p>
<h3>Limit image size</h3>
<p>As far as I understood, we can’t be proactive and limit the image size at the time the user is taking or picking it but we could limit it afterwards by adding a simple check on the file size. For example we could extend the above code with a check on a maximum file size of 10 Mb (the size as to be provided in bytes):</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">if</span><span style="color:#F8F8F2"> (myFile.size </span><span style="color:#FF79C6">></span><span style="color:#BD93F9"> 10485760</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#50FA7B">    reject</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Image is too big (max. 10 Mb)</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><h3>Upload the image to Firebase Storage</h3>
<p>Like I said in my introduction, we are still developing our upcoming editor for presentations and we recently underwent, kind of, our third migration 😅 For the edition we are now using <a href="https://firebase.google.com/docs/firestore" rel="noopener noreferrer external">Firestore</a> and since we are already there, we decided to host user personal assets in Firebase Storage.</p>
<p>I won’t show you how to initialize Firebase but here’s how you could upload the image. Nothing easier, just grab a reference on your storage providing the directory and filename where you want to save the file and upload it.</p>
<p>If you don’t want to change the filename, respectively keep it as its original name, you could use the attribute of the <code>file</code> called <code>name</code> which is kind of handy as it contains the extension too.</p>
<p>Afterwards, if you wish to display the result in an <code>img</code> element for example, or even better in an open source Web Component to lazy load the image like the one we have developed for <a href="https://docs.deckdeckgo.com/components/lazy-img" rel="noopener noreferrer external">DeckDeckGo</a> 😉, as long as your Firebase rules allows it, you could use a download url which you could obtain from the storage reference.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {firebase} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@firebase/app</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@firebase/storage</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">private </span><span style="color:#50FA7B">upload</span><span style="color:#F8F8F2">(): </span><span style="color:#8BE9FD;font-style:italic">Promise</span><span style="color:#FF79C6">&#x3C;void></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    return new </span><span style="color:#50FA7B">Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">void</span><span style="color:#F8F8F2">>(</span><span style="color:#FFB86C;font-style:italic">async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">reject</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">        const</span><span style="color:#F8F8F2"> filePicker </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> document.</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">input</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#50FA7B">        if</span><span style="color:#F8F8F2"> (!</span><span style="color:#FFB86C;font-style:italic">filePicker</span><span style="color:#F8F8F2"> || !filePicker.files</span></span>
<span class="line"><span style="color:#F8F8F2">            || filePicker.files.length &#x3C;</span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#50FA7B">            reject</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">No file selected.</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">        const</span><span style="color:#F8F8F2"> myFile </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> filePicker.files[</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">        try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">          const</span><span style="color:#F8F8F2"> storagePathAndFilename </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#F1FA8C">             `myFolder/mySubfolders/</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">myFile.name</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">          const</span><span style="color:#F8F8F2"> ref </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#F8F8F2">             firebase.</span><span style="color:#50FA7B">storage</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">ref</span><span style="color:#F8F8F2">(storagePathAndFilename);</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">          await</span><span style="color:#8BE9FD;font-style:italic"> ref</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">put</span><span style="color:#F8F8F2">(</span><span style="color:#8BE9FD;font-style:italic">myFile</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">          const</span><span style="color:#F8F8F2"> myDownloadUrl </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> ref.</span><span style="color:#50FA7B">getDownloadURL</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">          console</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">log</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`Your image url is </span><span style="color:#FF79C6">${</span><span style="color:#8BE9FD;font-style:italic">myDownloadUrl</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">          resolve</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">        } catch (err) {</span></span>
<span class="line"><span style="color:#50FA7B">          reject</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">err</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><h3>Convert the image to a base64 string</h3>
<p>You might want to display the picture which was taken or selected directly or just convert it to a base64 string as you would need this format to submit it to your storage. For that purpose you use a <code>FileReader</code> as displayed hereafter.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">private </span><span style="color:#50FA7B">upload</span><span style="color:#F8F8F2">(): </span><span style="color:#8BE9FD;font-style:italic">Promise</span><span style="color:#FF79C6">&#x3C;void></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    return new </span><span style="color:#50FA7B">Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">void</span><span style="color:#F8F8F2">>(</span><span style="color:#FFB86C;font-style:italic">async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">reject</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">        const</span><span style="color:#F8F8F2"> filePicker </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> document.</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">input</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#50FA7B">        if</span><span style="color:#F8F8F2"> (!</span><span style="color:#FFB86C;font-style:italic">filePicker</span><span style="color:#F8F8F2"> || !filePicker.files</span></span>
<span class="line"><span style="color:#F8F8F2">            || filePicker.files.length &#x3C;</span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#50FA7B">            reject</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">No file selected.</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">        const</span><span style="color:#F8F8F2"> myFile </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> filePicker.files[</span><span style="color:#BD93F9">0</span><span style="color:#F8F8F2">];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">        const</span><span style="color:#F8F8F2"> myBase64File </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">convert</span><span style="color:#F8F8F2">(myFile);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">        console</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">log</span><span style="color:#F8F8F2">(</span><span style="color:#F1FA8C">`Your base64 image is </span><span style="color:#FF79C6">${</span><span style="color:#8BE9FD;font-style:italic">myBase64File</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#50FA7B">        resolve</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">private</span></span>
<span class="line"><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">myFile</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> File</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#FF79C6"> |</span><span style="color:#FFB86C;font-style:italic"> ArrayBuffer</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#FF79C6"> |</span><span style="color:#FFB86C;font-style:italic"> ArrayBuffer</span><span style="color:#F8F8F2">>((</span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">reject</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> fileReader</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> FileReader</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> FileReader</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (fileReader </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> myFile) {</span></span>
<span class="line"><span style="color:#F8F8F2">            fileReader.</span><span style="color:#50FA7B">readAsDataURL</span><span style="color:#F8F8F2">(myFile);</span></span>
<span class="line"><span style="color:#F8F8F2">            fileReader.</span><span style="color:#50FA7B">onload</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">                resolve</span><span style="color:#F8F8F2">(fileReader.result);</span></span>
<span class="line"><span style="color:#F8F8F2">            };</span></span>
<span class="line"><span style="color:#F8F8F2">            fileReader.</span><span style="color:#50FA7B">onerror</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">error</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">                reject</span><span style="color:#F8F8F2">(error);</span></span>
<span class="line"><span style="color:#F8F8F2">            };</span></span>
<span class="line"><span style="color:#F8F8F2">        } </span><span style="color:#FF79C6">else</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">            reject</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">No file provided</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><h3>Convert the image to a blob and create a local object URL</h3>
<p>I had a chat this morning with <a href="https://medium.com/@sergey.rudenko" rel="noopener noreferrer external">Sergey Rudenko</a> who pointed out the fact that converting the image to a blob in order to create and use a local object url, instead of base64, might improve the performance in special cases. That’s why he provided me the following alternative, which might interest you too.</p>
<p>Kudos Sergey and thank your for this nice add-ons 👍</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">private </span><span style="color:#50FA7B">convert</span><span style="color:#F8F8F2">(myFile: File): </span><span style="color:#8BE9FD;font-style:italic">Promise</span><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2">string</span><span style="color:#FF79C6">></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        return new </span><span style="color:#50FA7B">Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">>((</span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">reject</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">            const</span><span style="color:#F8F8F2"> fileReader </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> FileReader</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#50FA7B">            if</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">fileReader</span><span style="color:#F8F8F2"> &#x26;&#x26; </span><span style="color:#FFB86C;font-style:italic">myFile</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">                fileReader.</span><span style="color:#50FA7B">readAsDataURL</span><span style="color:#F8F8F2">(myFile);</span></span>
<span class="line"><span style="color:#F8F8F2">                fileReader.</span><span style="color:#50FA7B">onload</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">                    const</span><span style="color:#F8F8F2"> blob </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Blob</span><span style="color:#F8F8F2">([</span><span style="color:#FF79C6;font-weight:bold">new</span><span style="color:#50FA7B"> Uint8Array</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#F8F8F2">                               fileReader.result </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> ArrayBuffer</span><span style="color:#F8F8F2">)]);</span></span>
<span class="line"><span style="color:#FF79C6">                    const</span><span style="color:#F8F8F2"> blobURL </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> URL</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">createObjectURL</span><span style="color:#F8F8F2">(blob);</span></span>
<span class="line"><span style="color:#50FA7B">                    resolve</span><span style="color:#F8F8F2">(blobURL);</span></span>
<span class="line"><span style="color:#F8F8F2">                };</span></span>
<span class="line"><span style="color:#F8F8F2">                fileReader.</span><span style="color:#50FA7B">onerror</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">error</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">                    reject</span><span style="color:#F8F8F2">(error);</span></span>
<span class="line"><span style="color:#F8F8F2">                };</span></span>
<span class="line"><span style="color:#F8F8F2">            } </span><span style="color:#8BE9FD;font-style:italic">else</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">                reject</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">No file provided</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span></code></pre><h3>Cherry on the cake 🍒🎂</h3>
<p>This post is the outcome from a real use case and as our platform is open source, you are most welcomed to have a look at our code and even better, most welcomed to send us a PR if you do notice any possible improvements 🙏</p>
<p>You could find the code related code at the following address, it is where it starts respectively where I declare the <code>input</code> :</p>
<p><a href="https://github.com/deckgo/deckdeckgo/blob/4030608803118d87ef7cd40bdbd6e1382d64211f/studio/src/app/modals/editor/app-custom-images/app-custom-images.tsx#L234" rel="noopener noreferrer external">https://github.com/deckgo/deckdeckgo/blob/4030608803118d87ef7cd40bdbd6e1382d64211f/studio/src/app/modals/editor/app-custom-images/app-custom-images.tsx#L234</a></p>
<p>To infinity and beyond 🚀</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Angular services without Angular? Thank you Typescript]]></title>
          <description><![CDATA[Create a singleton service with Typescript to narrow specific functions or share data across components]]></description>
          <link>https://daviddalbusco.com/blog/angular-services-without-angular-thank-you-typescript/</link>
          <pubDate>Sun, 30 Jun 2019 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*Snr-Scxt2w2y3LJa59HX9Q.jpeg" alt="" / loading="lazy"><br />
<em>Not related, it’s just that my mum is cooking french fries next to me right now — Photo by <a href="https://unsplash.com/@louishansel?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Louis Hansel</a> on <a href="https://unsplash.com/search/photos/fries?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>One thing we could all agree on, I think, is the fact that using <a href="https://angular.io/guide/architecture-services" rel="noopener noreferrer external">Angular services</a> is just super duper easy. They are a great way to narrow specific functions and are straight forward to understand.</p>
<p>Per default, each service exists once in memory, when the app is running, which typically allow us to share data across components. This concept follows the <a href="https://en.wikipedia.org/wiki/Singleton_pattern" rel="noopener noreferrer external">singleton pattern</a> or at least it is they way I always understand these.</p>
<p>Whether in the case of a simple <a href="https://nodejs.org/en/" rel="noopener noreferrer external">Node.js</a> projects or in a Progressive Web Apps developed with <a href="https://stenciljs.com/" rel="noopener noreferrer external">Stencil</a> I recently developed, I didn’t used Angular but I had to implement this particular concept.</p>
<p>Fortunately I was always using <a href="https://www.typescriptlang.org/" rel="noopener noreferrer external">Typescript</a> which allowed me to achieve my goal. That’s why I thought about sharing the outcome in this short new blog post.</p>
<h3>Implementation of a singleton service with Typescript</h3>
<p>Typescript is somehow really close to Java, particularly in this specific solution. To implement a singleton we create a class with a constructor set as <strong>private</strong> to not allow “external” instantiations. Doing so, we create then our own “entry point generator” which we are going to declare as a <strong>static</strong> method (static being variables or functions shared between all instances) in order to create and serve only one <strong>static</strong> instance of our service.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> SingletonService</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    private</span><span style="color:#FF79C6"> static</span><span style="color:#F8F8F2"> instance</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> SingletonService</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    private</span><span style="color:#FF79C6"> constructor</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    static</span><span style="color:#50FA7B"> getInstance</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">SingletonService.instance) {</span></span>
<span class="line"><span style="color:#F8F8F2">            SingletonService.instance </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> SingletonService</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> SingletonService.instance;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><h3>Usage of a singleton service with Typescript</h3>
<p>As we have set the constructor as private, we won’t be able to instantiate the service as would we would normally do.</p>
<p>For example, the following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> myService</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> SingletonService</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> SingletonService</span><span style="color:#F8F8F2">();</span></span></code></pre><p>would result in the following error:</p>
<blockquote>
<p>TS2673: Constructor of class ‘SingletonService’ is private and only accessible within the class declaration.</p>
</blockquote>
<p>That’s why we created our own “entry point generator” and why the correct instantiation of such a singleton service should be implemented like the following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> myService</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> SingletonService</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> SingletonService.</span><span style="color:#50FA7B">getInstance</span><span style="color:#F8F8F2">();</span></span></code></pre><p>That’s it, nothing more, nothing less, we have created a single service available for all our components 🎉</p>
<h3>Cherry on the cake 🍒🎂</h3>
<p>As I said in my introduction, I recently often implemented this singleton pattern concept. I noticed that it particularly works well together with <a href="https://rxjs-dev.firebaseapp.com/" rel="noopener noreferrer external">RxJs</a> and if you are looking for an example, you could have a look to the <a href="https://docs.deckdeckgo.com/" rel="noopener noreferrer external">documentation</a> of our open source project <a href="https://deckdeckgo.com/" rel="noopener noreferrer external">DeckDeckGo</a>.</p>
<p>Concretely, here’s an implementation of a singleton service:</p>
<p>https://github.com/deckgo/deckdeckgo/blob/master/docs/src/app/services/menu/menu.service.tsx</p>
<p>And here’s an implementation of its usage:</p>
<p>https://github.com/deckgo/deckdeckgo/blob/1932ecfae03846f09a642858ba85e2fdccd666b4/docs/src/app/app-root.tsx#L19</p>
<p>If you’ve got any idea of improvements, please ping me as the above is the solution we are using in our upcoming web editor for presentations.</p>
<p>To infinity and beyond 🚀</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Create a Web Component to lazy load images using Intersection Observer]]></title>
          <description><![CDATA[How to create step by step a Web Component with StencilJS to lazy load images using the Intersection Oberserver API]]></description>
          <link>https://daviddalbusco.com/blog/create-a-web-component-to-lazy-load-images-using-intersection-observer/</link>
          <pubDate>Sat, 15 Jun 2019 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*ZR2kkYSQYyq97uwcbukDTQ.jpeg" alt="A lazy cow" / loading="lazy"></p>
<p>One of the many improvements introduced in the version <a href="https://webkit.org/blog/8718/new-webkit-features-in-safari-12-1/" rel="noopener noreferrer external">12.1</a> of Safari is the support for the Intersection Observer. Thanks to this enhancement, all <a href="https://caniuse.com/#search=IntersectionObserver" rel="noopener noreferrer external">modern browsers</a> are now supporting this API and it is therefore possible to detect when a particular element would become visible with the exact same piece code (<a href="https://caniuse.com/#search=IntersectionObserver" rel="noopener noreferrer external">almost</a>) everywhere.</p>
<p>As Web Components could be use in any frameworks or even without, respectively could be use everywhere too, I thought it would be cool to display how to create such a component in order to lazy load images with the help of the Intersection Observer API.</p>
<p><em>When it comes to Web Components, I have a bit more experience with the <a href="https://stenciljs.com/" rel="noopener noreferrer external">StencilJS</a> compiler as I’m currently using it to develop our upcoming o en source editor for PWA presentations, <a href="https://deckdeckgo.com/" rel="noopener noreferrer external">DeckDeckGo</a>. That’s why I will use it for the purpose of this tutorial. If you never used Stencil yet, no worries, this tutorial is a step by step, you don’t need to know it to go thru the post and I even hope you will like to discover it. I personally think it’s a lot of fun to play with StencilJS</em></p>
<h3>Getting started</h3>
<p>To begin our (short) journey, we are going to create a new Web Component using StencilJS. For that purpose, run <code>npm init stencil</code> in a terminal and follow the prompt:</p>
<p><img src="https://daviddalbusco.com/assets/images/1*Pvfa6ISTSDMmQb0T9ZtN-w.png" alt="" / loading="lazy"><br />
<span class="figcaption_hack">Run the initial command</span></p>
<p><img src="https://daviddalbusco.com/assets/images/1*KiMpOh_Tkc-c-lbQtshueQ.png" alt="" / loading="lazy"><br />
<span class="figcaption_hack">Select “component” as we want to develop a library</span></p>
<p><img src="https://daviddalbusco.com/assets/images/1*OrBL_5unZ5Ll8F3adWlBcA.png" alt="" / loading="lazy"><br />
<span class="figcaption_hack">Give a name to the component and project, like “lazy-load-img”</span></p>
<p><em>Note: To shorten the tutorial I won’t cover here how you could rename the default namespace and component’s names, just ping me if you would have specific questions about it.</em></p>
<h3>Coding</h3>
<p>Let’s code (yes, already, starting a project with the Stencil CLI is a pretty fast process 😂). Open the file <code>src/components/my-component.tsx</code> in your favorite editor and remove the default code generated by the CLI. Once done, the file should looks like the following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Component } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@stencil/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  tag</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">my-component</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  styleUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">my-component.css</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  shadow</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> MyComponent</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><h4>Rendering an image tag</h4>
<p>Now that our component is empty we could begin adding the first relevant piece of code, a <code>property</code> for the URL of the image, in order to be able to specify it using an attribute when we will use the component, and a <code>render</code> method which, well, will render the image when the component will be used:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Component, Prop} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@stencil/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  tag</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">my-component</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  styleUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">my-component.css</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  shadow</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> MyComponent</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  @</span><span style="color:#50FA7B">Prop</span><span style="color:#F8F8F2">() imgSrc</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FF79C6">img</span><span style="color:#50FA7B;font-style:italic"> data-src</span><span style="color:#FF79C6">={</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.imgSrc</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">/>;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>As you could notice we **didn’t **specified the correct image’s attribute <code>src</code> but rather used a pseudo-attribute <code>data-src</code> . Doing so, we are avoiding the automatic loading of the image, as the browser won’t find a source to load. We will later on take care of loading it correctly respectively we will defer the loading of the image, we will take care to lazy load the image.</p>
<h4>Intersection Observer</h4>
<p>At this point, our component is able to render an image (tag at least), we should now bind an Intersection Observer on this tag in order to detect when the image will be visible in the viewport (think for example about a huge scrollable page, we want to detect when an image, which is positioned at the far bottom of the page, will after a bit of scrolling become part of the viewable viewport/window/browser).</p>
<p>To bind our observer, we gonna use the event <code>componentDidLoad</code> which, well, is triggered when the component is loaded. In that particular function we will then search for our image and bind the Intersection Observer.</p>
<p><em>Note: our component is shadowed, that’s why we are querying the element on its shadow root respectively <code>this.el.shadowRoot.querySelector</code> and not <code>this.el.querySelector</code> as it would be the case if the the component would not be shadowed.</em></p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Element</span><span style="color:#F8F8F2">() el: HTMLElement;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">private observer: IntersectionObserver;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">componentDidLoad</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> img</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLImageElement</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">                 this</span><span style="color:#F8F8F2">.el.shadowRoot.</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">img</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  if</span><span style="color:#F8F8F2"> (img) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.observer </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> IntersectionObserver</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.onIntersection);</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.observer.</span><span style="color:#50FA7B">observe</span><span style="color:#F8F8F2">(img);</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>In this particular example I didn’t provided any options to the Intersection Observer beside the function <code>this.onIntersection</code> which we are about to declare. If you wish to know a bit more about these, there is a nice and easy post entry on the <a href="https://webkit.org/blog/8582/intersectionobserver-in-webkit/" rel="noopener noreferrer external">Webkit blog</a> about it.</p>
<h4>Loading</h4>
<p>We could now write the final piece of our code, the function which is triggered when an element will become visible in the viewport respectively <code>onIntersection</code> . This method will do nothing more than receiving the element which is now ready to be intercepted, check if we already triggered manually the display of the element (by just checking if the <code>data-src</code> attribute is still there) and if not, will display the image (by converting the <code>data-src</code> attribute into the proper image source attribute <code>src</code> ).</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">private </span><span style="color:#50FA7B">onIntersection</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">entries</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  for</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> entry </span><span style="color:#FF79C6">of</span><span style="color:#F8F8F2"> entries) {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (entry.isIntersecting) {</span></span>
<span class="line"><span style="color:#FF79C6">      if</span><span style="color:#F8F8F2"> (</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.observer) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">         this</span><span style="color:#F8F8F2">.observer.</span><span style="color:#50FA7B">disconnect</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">      }</span></span>
<span class="line"><span style="color:#FF79C6">      if</span><span style="color:#F8F8F2"> (entry.target.</span><span style="color:#50FA7B">getAttribute</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">data-src</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">)) {</span></span>
<span class="line"><span style="color:#F8F8F2">        entry.target.</span><span style="color:#50FA7B">setAttribute</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">src</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">                        entry.target.</span><span style="color:#50FA7B">getAttribute</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">data-src</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">));</span></span>
<span class="line"><span style="color:#F8F8F2">        entry.target.</span><span style="color:#50FA7B">removeAttribute</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">data-src</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">      }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><h4>All together</h4>
<p>Summarized, the all code of our component contains:</p>
<ul>
<li>A <code>render</code> method to well, render our image but first without <code>src</code> to “block” the automatic loading of the image</li>
<li>A <code>componentDidLoad</code> method where we hook on the component state to initialize the Intersection Observer for our image</li>
<li>A custom function <code>onIntersection</code> to detect when the component, respectively, the image would become visible in the viewport and to effectively trigger/start displaying it</li>
</ul>
<p>All together our component’s code should look like the following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Component, Prop, Element} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@stencil/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  tag</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">my-component</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  styleUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">my-component.css</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  shadow</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> MyComponent</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  @</span><span style="color:#50FA7B">Element</span><span style="color:#F8F8F2">() el</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLElement</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  @</span><span style="color:#50FA7B">Prop</span><span style="color:#F8F8F2">() imgSrc</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#F8F8F2"> observer</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> IntersectionObserver</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  componentDidLoad</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> img</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLImageElement</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">               this</span><span style="color:#F8F8F2">.el.shadowRoot.</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">img</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (img) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">      this</span><span style="color:#F8F8F2">.observer </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> IntersectionObserver</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.onIntersection);</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">      this</span><span style="color:#F8F8F2">.observer.</span><span style="color:#50FA7B">observe</span><span style="color:#F8F8F2">(img);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#50FA7B"> onIntersection</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">entries</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    for</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> entry </span><span style="color:#FF79C6">of</span><span style="color:#F8F8F2"> entries) {</span></span>
<span class="line"><span style="color:#FF79C6">      if</span><span style="color:#F8F8F2"> (entry.isIntersecting) {</span></span>
<span class="line"><span style="color:#FF79C6">         if</span><span style="color:#F8F8F2"> (</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.observer) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">             this</span><span style="color:#F8F8F2">.observer.</span><span style="color:#50FA7B">disconnect</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">         }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">         if</span><span style="color:#F8F8F2"> (entry.target.</span><span style="color:#50FA7B">getAttribute</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">data-src</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">)) {</span></span>
<span class="line"><span style="color:#F8F8F2">             entry.target.</span><span style="color:#50FA7B">setAttribute</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">src</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">                        entry.target.</span><span style="color:#50FA7B">getAttribute</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">data-src</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">));</span></span>
<span class="line"><span style="color:#F8F8F2">             entry.target.</span><span style="color:#50FA7B">removeAttribute</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">data-src</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">         }</span></span>
<span class="line"><span style="color:#F8F8F2">      }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">  };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FF79C6">img</span><span style="color:#50FA7B;font-style:italic"> data-src</span><span style="color:#FF79C6">={</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.imgSrc</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">/>;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><h3>Testing</h3>
<p>StencilJS comes with a handy [development server](https://stenciljs.com/docs/dev server/) which we are going to use to test our component. Before starting it, we are going to edit the file <code>src/index.html</code> in order to consume our component multiple times as we want to simulate a long scrollable page. You could for example edit this Html page and add multiple times the same image. Furthermore, we are are going to define a bit of styling on our component to let the browser understand which images are in the viewport and which aren’t.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">&#x3C;!</span><span style="color:#F8F8F2">DOCTYPE html</span><span style="color:#FF79C6">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">html</span><span style="color:#50FA7B;font-style:italic"> dir</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">ltr</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">en</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">head</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;</span><span style="color:#FF79C6">meta</span><span style="color:#50FA7B;font-style:italic"> charset</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">utf-8</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;</span><span style="color:#FF79C6">meta</span><span style="color:#50FA7B;font-style:italic"> name</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">viewport</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> content</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=5.0</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;</span><span style="color:#FF79C6">title</span><span style="color:#F8F8F2">>Stencil Component Starter&#x3C;/</span><span style="color:#FF79C6">title</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#50FA7B;font-style:italic"> src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/build/mycomponent.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;</span><span style="color:#FF79C6">style</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    my-component </span><span style="color:#FF79C6">{</span></span>
<span class="line"><span style="color:#F8F8F2">      display: block;</span></span>
<span class="line"><span style="color:#F8F8F2">      height: 1000px;</span></span>
<span class="line"><span style="color:#F8F8F2">      width: </span><span style="color:#BD93F9">100</span><span style="color:#FF79C6">%</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">    }</span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;/</span><span style="color:#FF79C6">style</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">head</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">body</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;</span><span style="color:#8BE9FD;font-style:italic">my-component</span><span style="color:#50FA7B;font-style:italic"> img-src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://deckdeckgo.com/assets/img/deckdeckgo.png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">my-component</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;</span><span style="color:#8BE9FD;font-style:italic">my-component</span><span style="color:#50FA7B;font-style:italic"> img-src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://deckdeckgo.com/assets/img/deckdeckgo.png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">my-component</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;</span><span style="color:#8BE9FD;font-style:italic">my-component</span><span style="color:#50FA7B;font-style:italic"> img-src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://deckdeckgo.com/assets/img/deckdeckgo.png</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">my-component</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;!-- Etc. add multiple times the component --></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">body</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">html</span><span style="color:#F8F8F2">></span></span></code></pre><p>Once edited, we could now start the development server with the command <code>npm run start</code></p>
<p><img src="https://daviddalbusco.com/assets/images/1*1EIqxblllapum3l6BQpvgQ.png" alt="" / loading="lazy"><br />
<span class="figcaption_hack">Start the development server</span></p>
<p>And once executed, open Chrome or Safari at the address <code>http://localhost:3333</code> and test your lazy loading component 🎉</p>
<p><img src="https://daviddalbusco.com/assets/images/1*LmehcptdT_bkoCqFtui0ew.gif" alt="" / loading="lazy"></p>
<p>As you could notice in this animated gif above, I added a console output to display in the debugger when images are effectively loaded. At first, only one image is loaded but when I began to scroll, the other images began to be loaded as soon as they appear in the viewport (see the counter in the debugger)</p>
<h3>What’s next</h3>
<p>Our component is pretty neat but you may want to improve it with some Intersection Observer options and also with some more properties or CSS4 variables for the shadowed image of the component, like the <code>alt</code> , <code>width</code> and <code>height</code> attributes.</p>
<p><em>If you have specific questions regarding that particular subject or maybe even want me to write a follow-up blog post to describe these steps, let me now!</em></p>
<h3>Cherry on the cake 🍒🎂</h3>
<p>Like I said in my introduction, I did implement such a component for our upcoming open source editor for PWA presentations called DeckDeckGo. Therefore you could find my implementation on GitHub.</p>
<p><a href="https://github.com/deckgo/deckdeckgo/tree/master/studio/src/app/components/feed/card/app-feed-lazy-img" rel="noopener noreferrer external">https://github.com/deckgo/deckdeckgo/tree/master/studio/src/app/components/feed/card/app-feed-lazy-img</a></p>
<p>Our editor is now yet live online, but if you wish to give us a hand to test it, you are most welcomed to join our <a href="https://deckdeckgo.com/?index=2" rel="noopener noreferrer external">beta program</a>. Finally, if you have any ideas of improvements, don’t hesitate to ping me, that would be super neat 🤙</p>
<p>To infinity and beyond 🚀</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[How to implement the Web Share API with a fallback]]></title>
          <description><![CDATA[How to implement the Web Share API with a fallback]]></description>
          <link>https://daviddalbusco.com/blog/how-to-implement-the-web-share-api-with-a-fallback/</link>
          <pubDate>Mon, 10 Jun 2019 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*rjWBYTwXqa5YsogRl5kPdg.jpeg" alt="" / loading="lazy"><br />
<em>Photo by <a href="https://unsplash.com/@noiseporn?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Noiseporn</a> on <a href="https://unsplash.com/search/photos/social-share?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>In the past weeks I often had to implement the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Navigator/share" rel="noopener noreferrer external">Web Share API</a> in several applications. As it is not yet <a href="https://caniuse.com/#search=web%20share%20api" rel="noopener noreferrer external">supported</a> by all browsers and devices, I always had to implement the same fallback, respectively I always used the open source Web Component I developed called “<a href="https://websocialshare.com/" rel="noopener noreferrer external">Web Social Share</a>” as a fallback. That’s why I’m sharing my solution with this new blog post, hoping that maybe someday it will help someone or even better, that maybe someone will ping me back with a better solution 😁</p>
<h3>Getting started</h3>
<p>The following implementation is pretty straight forward. We are going to implement a share method which detects if the Web Share API is supported by the browser or not. If supported it will, I guess you get it, use and open the Web Share API otherwise it will open the share fallback.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*6rNMJkG_C-N6B-5ibAL_8Q.gif" alt="" / loading="lazy"><br />
<em>Safari (left) supports Web Share API, Chrome (desktop, right) doesn’t support it and fallback to the Web Component “Web Social Share”</em></p>
<h3>Main function and detection</h3>
<p>First, we are going to implement a main share function which checks which share method should be use.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#50FA7B;font-style:italic"> language</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">JavaScript</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  openShare = async () => </span><span style="color:#FF79C6">{</span></span>
<span class="line"><span style="color:#50FA7B">    if</span><span style="color:#F8F8F2"> (navigator </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> navigator.share) {</span></span>
<span class="line"><span style="color:#F8F8F2">      await </span><span style="color:#50FA7B">shareNative</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    } else {</span></span>
<span class="line"><span style="color:#F8F8F2">      await </span><span style="color:#50FA7B">shareFallback</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#FF79C6">  }</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span></code></pre><h3>Web Share API</h3>
<p>We are now going to implement the above method called <code>shareNative()</code> which will use the Web Share API.</p>
<p>This API accepts three parameters currently: <code>url</code> , <code>text</code> and <code>title</code> . Only one of these parameters should at least be provided but I suggest you to use <code>text</code> and <code>url</code> . Using these two will for example allow you to add a nice message when a user would share an URL on Twitter.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*q6V5upxh3zwklbrMajj9fQ.jpeg" alt="" / loading="lazy"></p>
<p>The Web Share API is promised based and we are going to implement it like the following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">function</span><span style="color:#50FA7B"> shareNative</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> shareUrl </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#F1FA8C">          `</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">window.location.protocol</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">//</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">window.location.host</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#F8F8F2"> navigator.</span><span style="color:#50FA7B">share</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">      text</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">How to implement the Web Share API and a fallback</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">      url</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> shareUrl,</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    resolve</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p><em>We are using <code>window.location</code> to build dynamically the url we are going to share. You could replace this piece of code with any valid URL as a string if you rather like.</em></p>
<h3>Web Social Share as Fallback</h3>
<p>The Web Component I developed need firstly to be consumed. You could install it through <a href="https://www.npmjs.com/package/web-social-share" rel="noopener noreferrer external">npm</a> but in this tutorial we are just going to use it with the help of <a href="https://unpkg.com" rel="noopener noreferrer external">Unpkg</a>. For that purpose, we are going to add the following scripts to our HTML page:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#50FA7B;font-style:italic"> type</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">module</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://unpkg.com/web-social-share@latest/dist/websocialshare/websocialshare.esm.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#50FA7B;font-style:italic"> nomodule</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://unpkg.com/web-social-share@latest/dist/websocialshare/websocialshare.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span></code></pre><p>The component doesn’t ship any icons respectively we will have to provide our own. I designed the component that way because I thought that it makes more sense to not “force” the developer to use an icon which might not fits its design. Therefore, in this tutorial, we are going to use the <a href="https://ionicons.com" rel="noopener noreferrer external">Ionicons</a> which we are going to consume using Unpkg too. Of course, if your application already contains its own set of icons, just skip this step.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#50FA7B;font-style:italic"> src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://unpkg.com/ionicons@latest/dist/ionicons.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span></code></pre><h4>Declaration</h4>
<p>In opposition to the Web Share API, this component can’t guess which methods are available on the device where it runs. That’s why it only exposes a couple of share methods. In this tutorial we are going too implement the options <code>Email</code>, <code>Twitter</code> and <code>WhatsApp</code>.</p>
<p>To declare the component we are going to add it to the page, specifying that per default it should not be displayed ( <code>show=&quot;false&quot;</code> ). We are also going to specify the icons we want to use. Note that these are passed to the component using <code>slot</code> and could be inline styled.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">web-social-share</span><span style="color:#50FA7B;font-style:italic"> show</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">false</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-icon</span><span style="color:#50FA7B;font-style:italic"> name</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">logo-twitter</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> ariaLabel</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Twitter</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> slot</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">twitter</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            style</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">color: #00aced;</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ion-icon</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-icon</span><span style="color:#50FA7B;font-style:italic"> name</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">mail</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> slot</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">email</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> ariaLabel</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Email</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            style</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">color: #ff8ea3;</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ion-icon</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-icon</span><span style="color:#50FA7B;font-style:italic"> name</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">logo-whatsapp</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> ariaLabel</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">WhatsApp</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            slot</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">whatsapp</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">            style</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">color: #25D366;</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">   &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ion-icon</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">web-social-share</span><span style="color:#F8F8F2">></span></span></code></pre><p><em>If you never use <code>slot</code> before and want to know a bit more about them, I wrote another article “<a href="https://medium.com/stencil-tricks/a-practical-introduction-to-styling-a-shadow-dom-and-slots-879565a2f423" rel="noopener noreferrer external">A practical introduction to styling a Shadow DOM and Slots</a>” which tries to explain practically how these could be used and styled.</em></p>
<h4>Implementation</h4>
<p>Now that we have consumed and declared the Web Component, we just have to implement the final piece with the implementation of the above mentioned method called <code>shareFallback()</code>.</p>
<p>This function will query the DOM for a reference on the component (using <code>document.querySelector</code> ), define the share options and finally will trigger the display of the share options by modifying its <code>show</code> attribute (the component listen for changes on this attribute to display or not its toolbar).</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">function</span><span style="color:#50FA7B"> shareFallback</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> webSocialShare </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#F8F8F2">          document.</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">web-social-share</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">webSocialShare </span><span style="color:#FF79C6">||</span><span style="color:#FF79C6"> !</span><span style="color:#F8F8F2">window) {</span></span>
<span class="line"><span style="color:#FF79C6">      return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> shareUrl </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#F1FA8C">          `</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">window.location.protocol</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">//</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">window.location.host</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> share </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">      displayNames</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">      config</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [{</span></span>
<span class="line"><span style="color:#F8F8F2">        twitter</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">          socialShareUrl</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> shareUrl,</span></span>
<span class="line"><span style="color:#F8F8F2">          socialSharePopupWidth</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 300</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">          socialSharePopupHeight</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> 400</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">      },{</span></span>
<span class="line"><span style="color:#F8F8F2">        email</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">          socialShareBody</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> shareUrl</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">      }, {</span></span>
<span class="line"><span style="color:#F8F8F2">        whatsapp</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">          socialShareUrl</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> shareUrl</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">      }]</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"><span style="color:#6272A4">    // The configuration, set the share options</span></span>
<span class="line"><span style="color:#F8F8F2">    webSocialShare.share </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> share;</span></span>
<span class="line"><span style="color:#6272A4">    // Show/open the share actions</span></span>
<span class="line"><span style="color:#F8F8F2">    webSocialShare.show </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    resolve</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>That’s it, we have implemented the Web Share API and a fallback 🎉</p>
<h3>Trying it out</h3>
<p>To try out the implementation, we could just add for example a <code>button</code> which calls <code>openShare()</code> when its clicked.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">button</span><span style="color:#50FA7B;font-style:italic"> onClick</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">openShare()</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Share&#x3C;/</span><span style="color:#FF79C6">button</span><span style="color:#F8F8F2">></span></span></code></pre><p>If everything went according plan, the result should looks like the following:</p>
<p><img src="https://daviddalbusco.com/assets/images/1*MtKK5WfJQJq0piMe1Gfj9Q.gif" alt="" / loading="lazy"><br />
<em>Again, Safari (left) supports Web Share API and Chrome (desktop) doesn’t and use the fallback</em></p>
<h3>Cherry on the cake 🍒🎂</h3>
<p>Like I said in my introduction, I implemented the above solution in several applications the past weeks, I notably implemented it in the <a href="https://deckdeckgo.com/" rel="noopener noreferrer external">website</a> and <a href="https://github.com/deckgo/deckdeckgo-starter" rel="noopener noreferrer external">starter kit</a> of DeckDeckGo, our upcoming open source web editor for presentations. Therefore don’t hesitate to have a look at the code and to ping me if you have a better solution to share or maybe even have a PR to submit to improves it 😉</p>
<p>To infinity and beyond 🚀</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Create a Web Component to display a random photo from Unsplash]]></title>
          <description><![CDATA[A tutorial to create with StencilJS your own Web Component which display a random photo from Unsplash]]></description>
          <link>https://daviddalbusco.com/blog/create-a-web-component-to-display-a-random-photo-from-unsplash/</link>
          <pubDate>Wed, 29 May 2019 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*TlvBM-zGT1x7SmVMlKyAuQ.jpeg" alt="" / loading="lazy"><br />
<em>Photo by <a href="https://unsplash.com/photos/QdqK4doOzcQ?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Mat Reding</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>I recently integrated the <a href="https://unsplash.com" rel="noopener noreferrer external">Unsplash</a> API to our upcoming open source web editor for presentations, <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>, and I thought that sharing my small learning experience in a new blog post might maybe, who knows, help someone in the future 😉 Therefore, in the following article, I will show you how to create your own Web Component to display a random photo from Unsplash.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*xjFa1A-sWUNKUnn9Lyh1kg.gif" alt="" / loading="lazy"><br />
<em>Our goal is to create such a component which fetch a new random photo on each browser refresh</em></p>
<h3>Web Components with StencilJS</h3>
<p>When it comes to Web Components, I’ve got a bit more experience with <a href="https://stenciljs.com" rel="noopener noreferrer external">StencilJS</a> as any other compilers as I’m developing DeckDeckGo with. That’s why I will use it for the purpose of this tutorial. If you never used it before, don’t be afraid, it’s super straight forward, really developer friendly and fun, give it a try!</p>
<h3>Unsplash vs Pixabay</h3>
<p>You might ask yourself why Unsplash instead of <a href="https://pixabay.com" rel="noopener noreferrer external">Pixabay</a> or any other stock photos services? Well first of all, I only compared these two 😂 Secondly, they both have their assets but Unsplash is better suited for our needs.</p>
<p>The main difference in the two services is the way they provide their images. If you use Unsplash, you have to use “<a href="https://help.unsplash.com/api-guidelines/more-on-each-guideline/guideline-hotlinking-images" rel="noopener noreferrer external">hotlink images</a>”, which means that images remains hosted by them. On the other side, if you would use the Pixabay API, you would have to self host the images (see <a href="https://pixabay.com/api/docs/" rel="noopener noreferrer external">“hotlinking“</a> in their documentation). Furthermore, if you would use Pixabay, you would have to cache every requests for 24 hours too, which I personally find a bit annoying to implement (no offense). That being said, Unsplash needs a bit of backend or cloud functions implementation too, as you have to keep your access key secret.</p>
<p>Like I said above, all in all, I found Unsplash better suited for our needs. But I tried both, you could even have a look at the <a href="https://github.com/deckgo/deckdeckgo/commit/d7798cafc93f7b7ca15e98034eb9ed5c12ffe764#diff-09fc8f8165c7427a9fb91c8b553dbd41" rel="noopener noreferrer external">commit</a> I did to migrate from one to the other 😄</p>
<h3>Prerequisite</h3>
<p>In order to use the Unsplash API, you would need to register yourself and your application in order to get an “Access Key”. These steps could be executed on their website <a href="https://unsplash.com/developers" rel="noopener noreferrer external">https://unsplash.com/developers</a>. It is straight forward, therefore I won’t document it in this blog post but if you would face any difficulties, don’t hesitate to ping me for any questions, I would be happy to help, if I am able to 😇</p>
<h3>Getting started</h3>
<p>To begin our journey, we are going to create a new Web Component using StencilJS with the command line <code>npm init stencil</code>.</p>
<p><em>In this particular post, I’m going to step the details. If you never kickstarted such a component before, you could have a look to the “Getting started” chapter of a <a href="https://dev.to/daviddalbusco/create-a-web-component-to-lazy-load-images-using-intersection-observer-45gf" rel="noopener noreferrer external">previous article</a> I wrote for more details.</em></p>
<p>Once the component created, we could open the file <code>src/components/my component.tsx</code> in our favorite editor and remove the default code generated by the CLI in order to have an empty class at our disposal:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> { Component, h } </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@stencil/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  tag</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">my-component</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  styleUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">my-component.css</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  shadow</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> MyComponent</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p><em>The imported <code>h</code> from <code>@stencil/core</code> is something new related to Stencil One. If you would use a previous Stencil version, just don’t import it.</em></p>
<h4>Rendering an image</h4>
<p>Our goal is to display a random stock photo from Unsplash, so guess what, we are now going to add an image to our component. Furthermore, as we are going to fetch its URI from the Unsplash API, we are going to define a variable which should contains this value.</p>
<p><em>If you are new to StencilJS, let me try to summarize. There are no Html templates in Stencil, it uses <a href="https://stenciljs.com/docs/templating-jsx/" rel="noopener noreferrer external">JSX</a>. Each component as a <code>render</code> function that returns what needs to be displayed. Furthermore, we are annotating our variable as <code>State()</code> telling the component that each modifications on this particular variable should trigger a new call of the <code>render</code> function.</em></p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Component, State, h} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@stencil/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  tag</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">my-component</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  styleUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">my-component.css</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  shadow</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> MyComponent</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  @</span><span style="color:#50FA7B">State</span><span style="color:#F8F8F2">() imgSrc</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.imgSrc </span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FF79C6">img</span><span style="color:#50FA7B;font-style:italic"> src</span><span style="color:#FF79C6">={</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.imgSrc</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">/> </span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><h4>Querying a random photo from Unsplash</h4>
<p>Unsplash provide wrappers for their API in various languages. For Javascript they notably provide the <a href="https://github.com/unsplash/unsplash-js" rel="noopener noreferrer external">unsplash-js</a> wrapper. I didn’t used it neither in our application nor in this tutorial. Firstly because it would not have really reduced the amount of work for me, as my requirements were really lean, and secondly because, mostly for performances reason, I rather like to have just a few line of codes in DeckDeckGo than a dependance on a all library which offers way more features than what we need.</p>
<h4>⚠️ <strong>Important</strong> ⚠️</h4>
<p><img src="https://daviddalbusco.com/assets/images/1*0rzkOPaCAk3buGtKj5ysjQ.gif" alt="" / loading="lazy"></p>
<p>For the purpose of this tutorial, I will display pieces of code where the Unsplash API is queried directly (https://api.unsplash.com/). <strong>DO NOT</strong> do that in any applications, real or not, that you would upload online. Your Unplash access key has to remain secret. Furthermore, don’t “forget” to give back <a href="https://unsplash.com/documentation#guidelines--crediting" rel="noopener noreferrer external">credits</a> to the authors of the photos.</p>
<h4>Fetching a random photo</h4>
<p>The Unsplash API exposes an endpoint “<a href="https://unsplash.com/documentation#get-a-random-photo" rel="noopener noreferrer external">random photo</a>”, which, guess what, would allow us to request and get a random photo from their collections 😜 Therefore we are just going to implement a function which perform this (HTTPS) getter and filter the results in order to find the hotlinked url of the image.</p>
<p><em>Unsplash provide different format (“full”, “raw”, ”regular”, “small” and “thumb”) of their images. In this example I used the “thumb” size.</em></p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">private </span><span style="color:#50FA7B">getRandomPhoto</span><span style="color:#F8F8F2">(): </span><span style="color:#8BE9FD;font-style:italic">Promise</span><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2">string</span><span style="color:#FF79C6">></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  return new </span><span style="color:#50FA7B">Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">>(</span><span style="color:#FFB86C;font-style:italic">async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">    const</span><span style="color:#F8F8F2"> searchUrl</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#E9F284">                     '</span><span style="color:#F1FA8C">https://api.unsplash.com/photos/random/</span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> +</span></span>
<span class="line"><span style="color:#F1FA8C">                     `?client_id=</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">YOUR_ACCESS_KEY</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">    try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">      const</span><span style="color:#F8F8F2"> rawResponse</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Response</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> fetch</span><span style="color:#F8F8F2">(searchUrl);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">      const</span><span style="color:#F8F8F2"> response </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">parse</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">await</span><span style="color:#F8F8F2"> rawResponse.</span><span style="color:#50FA7B">text</span><span style="color:#F8F8F2">());</span></span>
<span class="line"><span style="color:#50FA7B">      if</span><span style="color:#F8F8F2"> (!</span><span style="color:#FFB86C;font-style:italic">response</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#50FA7B">         resolve</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">undefined</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">         return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">      }</span></span>
<span class="line"><span style="color:#50FA7B">      resolve</span><span style="color:#F8F8F2">(response.urls &#x26;&#x26; response.urls.thumb ?</span></span>
<span class="line"><span style="color:#F8F8F2">              response.urls.thumb </span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> undefined</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    } catch (err) {</span></span>
<span class="line"><span style="color:#50FA7B">      resolve</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">undefined</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>We are interested to fetch automatically a photo. We could therefore hook on a component loading [lifecycle](https://stenciljs.com/docs/component lifecycle) and call our <code>getRandomPhoto</code> function to assign its result to the variable <code>imgSrc</code> we have defined before.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">async </span><span style="color:#50FA7B">componentWillLoad</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">  this</span><span style="color:#F8F8F2">.imgSrc </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">getRandomPhoto</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><h4>Track a photo download</h4>
<p>Our solution is almost ready but we have to add one last piece: Unsplash requires a trigger on their “<a href="https://unsplash.com/documentation#track-a-photo-download" rel="noopener noreferrer external">download</a>” endpoint when an image is downloaded (you could find more information about when and why this should happen in their <a href="https://help.unsplash.com/api-guidelines/guideline-triggering-a-download" rel="noopener noreferrer external">guidelines</a>). To call this end point we could create another (HTTPS) getter.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">private </span><span style="color:#50FA7B">registerDownload</span><span style="color:#F8F8F2">(photoId: string): </span><span style="color:#8BE9FD;font-style:italic">Promise</span><span style="color:#FF79C6">&#x3C;void></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  return new </span><span style="color:#50FA7B">Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">void</span><span style="color:#F8F8F2">>(</span><span style="color:#FFB86C;font-style:italic">async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">    const</span><span style="color:#F8F8F2"> downloadUrl</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#E9F284">         '</span><span style="color:#F1FA8C">https://api.unsplash.com/</span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> +</span></span>
<span class="line"><span style="color:#F1FA8C">         `photos/</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">photoId</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">/download/?client_id=</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">YOUR_ACCESS_KEY</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">    try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">      await</span><span style="color:#50FA7B"> fetch</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">downloadUrl</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">      resolve</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    } catch (err) {</span></span>
<span class="line"><span style="color:#50FA7B">      resolve</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Obviously, now that we have created a new function, we should call it 😆 As the endpoint requires the ID of the photo, we could call it from our previous function <code>getRandomPhoto</code> before we return the url of the image.</p>
<p><em>In a real implementation, I would first apply and render the url of the image and then call the download endpoint, but I thought that doing it this way for the purpose of this article would be make the code clearer to explain.</em></p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">registerDownload</span><span style="color:#F8F8F2">(response.id);</span></span></code></pre><p>That’s it, our component is ready 🎉</p>
<h4>All together</h4>
<p><img src="https://daviddalbusco.com/assets/images/1*IV5q7-l-VsZMkuKUiZ1giA.gif" alt="" / loading="lazy"></p>
<p>Before trying it out, let me just summarize all the component code’s at once:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Component, State, h} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@stencil/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  tag</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">my-component</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  styleUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">my-component.css</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  shadow</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> MyComponent</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  @</span><span style="color:#50FA7B">State</span><span style="color:#F8F8F2">() imgSrc</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  async</span><span style="color:#50FA7B"> componentWillLoad</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.imgSrc </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">getRandomPhoto</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#50FA7B"> getRandomPhoto</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">string</span><span style="color:#F8F8F2">>(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">      const</span><span style="color:#F8F8F2"> searchUrl</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#E9F284">                     '</span><span style="color:#F1FA8C">https://api.unsplash.com/photos/random/</span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> +</span></span>
<span class="line"><span style="color:#F1FA8C">                     `?client_id=</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">YOUR_ACCESS_KEY</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">     try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> rawResponse</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Response</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> await</span><span style="color:#50FA7B"> fetch</span><span style="color:#F8F8F2">(searchUrl);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> response </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">parse</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">await</span><span style="color:#F8F8F2"> rawResponse.</span><span style="color:#50FA7B">text</span><span style="color:#F8F8F2">());</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">response) {</span></span>
<span class="line"><span style="color:#50FA7B">          resolve</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">undefined</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">          return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">registerDownload</span><span style="color:#F8F8F2">(response.id);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">        resolve</span><span style="color:#F8F8F2">(response.urls </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> response.urls.thumb </span><span style="color:#FF79C6">?</span></span>
<span class="line"><span style="color:#F8F8F2">                response.urls.thumb </span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">      } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#50FA7B">        resolve</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">undefined</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">      }</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#50FA7B"> registerDownload</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">photoId</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">void</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">void</span><span style="color:#F8F8F2">>(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">      const</span><span style="color:#F8F8F2"> downloadUrl</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#E9F284">         '</span><span style="color:#F1FA8C">https://api.unsplash.com/</span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> +</span></span>
<span class="line"><span style="color:#F1FA8C">         `photos/</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">photoId</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">/download/?client_id=</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">YOUR_ACCESS_KEY</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">      try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        await</span><span style="color:#50FA7B"> fetch</span><span style="color:#F8F8F2">(downloadUrl);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">        resolve</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">      } </span><span style="color:#FF79C6">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#50FA7B">        resolve</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">      }</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.imgSrc </span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FF79C6">img</span><span style="color:#50FA7B;font-style:italic"> src</span><span style="color:#FF79C6">={</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.imgSrc</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">/> </span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><h3>Trying it out</h3>
<p>Good we are ready to test our component. StencilJS comes with a handy local test server, therefore just run <code>npm run start</code> in a command line and open your browser at the address <code>http://localhost:3333</code>. If everything works as expected, a random stock photo should be displayed and a new one should be fetched each time you would refresh your page.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*xjFa1A-sWUNKUnn9Lyh1kg.gif" alt="" / loading="lazy"></p>
<h3>Cherry on the cake 🍒🎂</h3>
<p>As I said in my introduction, we have integrated the Unsplash API in <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>, our upcoming open source web editor for presentations, to ease the integration of stock photos. This implementation isn’t really that different from what we implemented in this tutorial (instead of a random photo, we are just fetching a list). Therefore, as we are open source, if you are looking for a real example of implementation, you could just have a look at our <a href="https://github.com/deckgo/deckdeckgo" rel="noopener noreferrer external">source code</a> 😃</p>
<p><img src="https://daviddalbusco.com/assets/images/1*nCyubS5I7eIwuIB7_DseLQ.gif" alt="" / loading="lazy"></p>
<p>To infinity and beyond 🚀</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[A new dead simple open source Web Component to lazy load images]]></title>
          <description><![CDATA[A new dead simple open source Web Component to lazy load images developed with StencilJS]]></description>
          <link>https://daviddalbusco.com/blog/a-new-dead-simple-open-source-web-component-to-lazy-load-images/</link>
          <pubDate>Fri, 17 May 2019 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*fGVtZ0a_ki-Vg14YzJqKYQ.jpeg" alt="" / loading="lazy"><br />
<em>A lazy good girl or boy. Photo by <a href="https://unsplash.com/photos/Zwvxj3ytTHcutm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Brianna Santellan</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText" rel="noopener noreferrer external">Unsplash</a></em></p>
<p>Last month I published a blog post in which I tried to demonstrate how to <a href="https://dev.to/daviddalbusco/create-a-web-component-to-lazy-load-images-using-intersection-observer-45gf" rel="noopener noreferrer external">create a Web Component to lazy load images using Intersection Observer</a> and it turned out afterwards, that I actually had to use my own medicine several times while developing <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>, our upcoming open source editor for presentations.</p>
<p>That’s why we are happy to publish on <a href="https://www.npmjs.com/package/deckdeckgo-lazy-img" rel="noopener noreferrer external">npm</a> today a new dead simple<br />
open source Web Component, developed with <a href="https://stenciljs.com" rel="noopener noreferrer external">StencilJS</a>, to lazy load images in any modern web projects regardless of which framework (or none) you are using 😃</p>
<h3>But why?</h3>
<p><img src="https://daviddalbusco.com/assets/images/1*evsRlXzetp7FdEOK2ynL1Q.gif" alt="" / loading="lazy"><br />
<em>But why another Web Component to lazy load images?</em></p>
<p>You may ask yourself why we had to develop and publish a new Web Component to lazy load images when it’s so easy to just write a couple of lines of Javascript, using the <a href="https://developer.mozilla.org/fr/docs/Web/API/Intersection_Observer_API" rel="noopener noreferrer external">Intersection Observer API</a>, to already have images supporting lazy loading 🤔</p>
<p>In fact, as I said before, we are developing an open source web editor for presentations build with Web Components. To save your edited data, the slides of your presentations, we have to save the (HTML) content into the database. As we are developing our tool for speed, all components and notably images have to be lazy loaded. For that purpose, we are “playing” with the <code>src</code> attributes of the images, tweaking them “on and off” to tell the browser when exactly images should be fetched or not and that is the root cause of our problem.</p>
<p>As we were saving the content and manipulating the <code>img</code> elements of the DOM, we were not able to ensure that we were always saving images in a “not loaded state” in our database, without having to add a couple of extra functions and recursions, which, we were not agree to implement, as it would have added complexity and computation time to the process. That’s why we came up to the idea of using a custom Web Component to isolate the lazy loading process and therefore to always be able to sav and load images in and from our database in the correct state without any extra checks.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*6w47kZMDJi-TfOMt1LJd8Q.gif" alt="" / loading="lazy"><br />
<em>Funny gifs but it took us actually three days to figure out what was the<br />
solution</em> 😂</p>
<h3>Integration with a framework</h3>
<p>You could install our component in your project using the following npm command:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">npm i @</span><span style="color:#50FA7B;font-style:italic">deckdeckgo</span><span style="color:#FF79C6">/</span><span style="color:#50FA7B;font-style:italic">lazy</span><span style="color:#FF79C6">-</span><span style="color:#50FA7B;font-style:italic">img</span><span style="color:#FF79C6"> --</span><span style="color:#F8F8F2">save</span></span></code></pre><p>Once installed, you could integrate it according the needs of the framework you are using like described in the Stencil’s <a href="https://stenciljs.com/docs/overview" rel="noopener noreferrer external">documentation</a>.</p>
<p>Finally you could use the component easily in your project, for example by replacing your <code>img</code> tags with its tag, respectively <code>deckgo-lazy-img</code> (note that instead of <code>src</code> and <code>alt</code> you would have to use <code>img-src</code> and <code>img-alt</code>).</p>
<p>Summarized, you could use the component like the following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">deckgo-lazy-img</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    img-src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/assets/img/your_image.png</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    img-alt</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">My lazy loaded image</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">deckgo-lazy-img</span><span style="color:#F8F8F2">></span></span></code></pre><p>That’s it, your image is lazy loaded 😄</p>
<h3>Integration without a framework</h3>
<p>Our component could also be use in any projects where no framework would be use. For that purpose you would just use the component from a CDN, like for example from <a href="https://unpkg.com" rel="noopener noreferrer external">Unpkg</a>, and then consume it like displayed above.</p>
<p>Again, summarized, you could do the following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">&#x3C;!</span><span style="color:#F8F8F2">DOCTYPE html</span><span style="color:#FF79C6">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">html</span><span style="color:#50FA7B;font-style:italic"> lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">en</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">head</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#50FA7B;font-style:italic"> src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://unpkg.com/@deckdeckgo/lazy-img@latest/dist/deckdeckgo-lazy-img.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">head</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">body</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;</span><span style="color:#8BE9FD;font-style:italic">deckgo-lazy-img</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    img-src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">/assets/img/your_image.png</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    img-alt</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">My lazy loaded image</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">deckgo-lazy-img</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">body</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">html</span><span style="color:#F8F8F2">></span></span></code></pre><h3>Responsiveness</h3>
<p>Once this small blog post was published, I received a couple of feedbacks and notably one from <a href="https://twitter.com/slightlylate/status/1129465541167501313" rel="noopener noreferrer external">Alex Russell</a> who had the idea to introduce the support for <a href="https://developer.mozilla.org/enzUS/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images" rel="noopener noreferrer external">responsive images</a> with the use of attributes <code>srcset</code> and <code>sizes</code>. Of course I thought it was an awesome idea and therefore I already implemented it and published it to npm 🤪</p>
<h3>Cherries on the cake 🍒🎂</h3>
<p>For once in my small blog posts, there are more than just one cherry on the cake 😉</p>
<p>First of all, the component is open source and its code is available on our <a href="https://github.com/deckgo/deckdeckgo/tree/master/webcomponents/lazy-img" rel="noopener noreferrer external">GitHub repo</a>, therefore you could access it and improve it according your needs.</p>
<p>Secondly, I took a bit of time to document all its attributes and features which are available in our <a href="https://docs.deckdeckgo.com/components/lazy-img" rel="noopener noreferrer external">documentation website</a>.</p>
<p>Finally, there might be some use cases where you would like to trigger “manually” the loading of the images respectively earlier than the Intersection Oberserver would do. For that purpose, the component is exposing an asynchronous method <code>lazyLoad()</code> which would let you do so 😇</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> element </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> document.</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">deckgo-lazy-img</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">await</span><span style="color:#F8F8F2"> element.</span><span style="color:#50FA7B">lazyLoad</span><span style="color:#F8F8F2">();</span></span></code></pre><p>I hope this new dead simple Web Component might help you and don’t hesitate to ping me with any feedbacks or any ideas of improvements, specially as we are using it “for real” in a new super duper cool feature we might be able to showcase really soon 😜</p>
<p>To infinity and beyond 🚀</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[An Ionic page with a back or a menu button and my Swiss recipe to cook Rösti]]></title>
          <description><![CDATA[An Ionic page with a back or a menu button and my Swiss recipe to cook Rösti]]></description>
          <link>https://daviddalbusco.com/blog/an-ionic-page-with-a-back-or-a-menu-button-and-my-swiss-recipe-to-cook-rosti/</link>
          <pubDate>Sat, 11 May 2019 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*RLw_zF55AGJaTXkMOgwTNQ.png" alt="" / loading="lazy"></p>
<p>Imagine Switzerland, stunning mountains, green grass everywhere, sun is shining, you’ve got a view on the Matterhorn, some cows are sitting peacefully next door doing nothing and your are eating some Rösti with your friends.</p>
<p>Have you pictured this lovely time and view in your mind? Sure? Good because that isn’t at all today’s case 😂 I’m in Zürich, it’s super grey outside, it’s rainy, freaking cold and windy but the good point is, it gives me time to write a new blog post 😉</p>
<p>Earlier this week I published a trick I use often about “<a href="https://dev.to/daviddalbusco/how-to-close-ionic-modals-using-the-hardware-back-button-pi2" rel="noopener noreferrer external">How to close Ionic modals using the hardware back button</a>” and I thought that I would then share another quick one regarding how to display conditionally a back or menu button in a page of an application developed with <a href="https://ionicframework.com" rel="noopener noreferrer external">Ionic</a>.</p>
<h3>UX</h3>
<p>You might ask your self “why such a UX”? Well, you might want to list a page in your menu (navigation “root”) but in the same time, you might want to implement a direct link from another page to that particular page without letting you users become confused about how to come back to the caller page (navigation “forward”).</p>
<p>I guess you might be confused about my explanation 🤔 Therefore here’s what would happens if we would implement nothing particular. In both cases, navigation “root” or “forward”, the icon its action would remain linked with the menu.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*DyRRdLLV6VqXKgYvw_9Z6Q.gif" alt="" / loading="lazy"></p>
<p>And here’s what we are going to achieve:</p>
<p><img src="https://daviddalbusco.com/assets/images/1*pctwpurwlPWrzAkvZykqSw.gif" alt="" / loading="lazy"></p>
<h3>Implementation</h3>
<p>Our goal is to render an <code>ion-menu-button</code> or an <code>ion-back-button</code> according the navigation as Ionic components are just going to take care of the rest. Therefore, first thing we have to do, is to declare a <code>boolean</code> variable in our page/component to reflect these two states, for example a public boolean variable called <code>canGoBack</code> :</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">canGoBack: boolean </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">;</span></span></code></pre><p><em>Note: in this blog post I display an <a href="https://angular.io" rel="noopener noreferrer external">Angular</a> implementation</em></p>
<p>Once added, we could modify the <code>ion-header</code> of our page and add the two conditional buttons:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-header</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-toolbar</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-buttons</span><span style="color:#50FA7B;font-style:italic"> slot</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">start</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">      &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-back-button</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">*ngIf="canGoBack">&#x3C;/ion-back-button></span></span>
<span class="line"><span>      </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">&#x3C;ion-menu-button</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">*ngIf="!canGoBack">&#x3C;/ion-menu-button></span></span>
<span class="line"><span>    </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">&#x3C;/ion-buttons></span></span>
<span class="line"></span>
<span class="line"><span>    </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">&#x3C;ion-title></span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">      Nothing</span></span>
<span class="line"><span>    </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">&#x3C;/ion-title></span></span>
<span class="line"><span>  </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">&#x3C;/ion-toolbar></span></span>
<span class="line"><span style="color:#FF5555;font-style:italic;text-decoration:underline">&#x3C;/ion-header></span></span></code></pre><p>Finally (didn’t I told you, it’s a super quick solution), we just have to<br />
implement the code to affect to correct state on our variable when our<br />
page/component is loading. For that purpose, Ionic provides a handy<br />
<code>IonRouterOutlet</code> which let us know if there is a page in the stack to go back to or none.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Component, OnInit} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {IonRouterOutlet} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@ionic/angular</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    selector</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-nothing</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    templateUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">nothing.page.html</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    styleUrls</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">nothing.page.scss</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">]</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> NothingPage</span><span style="color:#FF79C6"> implements</span><span style="color:#8BE9FD;font-style:italic"> OnInit</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    canGoBack</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#FF79C6"> =</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    constructor</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">private</span><span style="color:#FFB86C;font-style:italic"> routerOutlet</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> IonRouterOutlet</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    ngOnInit</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        this</span><span style="color:#F8F8F2">.canGoBack </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.routerOutlet </span><span style="color:#FF79C6">&#x26;&#x26;</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">                         this</span><span style="color:#F8F8F2">.routerOutlet.</span><span style="color:#50FA7B">canGoBack</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>That’s it, we have implemented a conditional back or menu button and action 🎉</p>
<h3>Cherry on the cake 🍒🎂</h3>
<p>I like to always end my articles with a last paragraph “Cherry on the cake” where I give another hint or trick related to the content I displayed but in this particular article I don’t have specific in mind. Therefore, instead of something related to the above solution, here’s my dead simple recipe to cook your own Rösti:</p>
<p><img src="https://daviddalbusco.com/assets/images/1*bRHfgb21RXhVuUnEEj1nUQ.jpeg" alt="" / loading="lazy"><br />
<em>These kind of Rösti but with, optionally, smaller pieces of bacon</em></p>
<ol>
<li>Boil 500 grams to 1 kilo of potatoes (for two) and once ready, let them cool down. Note: that step is optional, if you don’t have time you could cook the meal with raw potatoes but it’s more yummy with boiled ones.</li>
<li>Chop an onion.</li>
<li>In a stove, warm up some oil and cook briefly the onion.</li>
<li>If you are in a hungry mood and not vegetarian, you could now add some small pieces or cubes of bacons in your stove and cook them too. Not necessary, it had a bit of fat to the recipe but it also add some taste, it’s up to you 😉</li>
<li>Peel the potatoes. In english I think you would use a tool called a “vegetable peeler” for that purpose, in french the tool is called an “économe”.</li>
<li>Chop the potatoes. For that step you will use a grater or shredder, French I call this tool a “une râpe”.</li>
<li>Tips and tricks: once peeled, if you wait too much before cooking your potatoes might turn “brownish-weird-grey-color” while cooking. To preserve them, instead of just sparing them on the side, if you are a bit slow like me, you could spare them in a boil of (cold) water which will preserve their “integrity”.</li>
<li>Add the chopped potatoes to your stove and gild them for 5 minutes.</li>
<li>Now, bring the heat of the stove down (“medium power”) and shape the potatoes like a cake (“a circle of 20–30 cm diameter and 5 cm height”) with a spatula centered in your stove and <strong>DON’T TOUCH THEM</strong> for the next 15 minutes, let them cook like this.</li>
<li>After that time, gonna be a bit tricky, put a flat dinner plate on the top of your potatoes and get them out of the fire <strong>REVERSED</strong> on your plate.</li>
<li>If you feel that your stove is now not enough oily, add a bit of oil in it.</li>
<li>Once done, add back the potatoes we reserved on the plate back on the side which is not yet cooked (get it, that’s why we reversed them)and cook them for another 15 minutes. <strong>IMPORTANT</strong>: don’t break the cake, the potatoes should remain together, so be gentle in your moves.</li>
<li>5 minutes before the end of the cooking, if you wish, you could cook one or two fried eggs in another stove which you could later add on the top of the Rösti. I sometimes do that extract step because the yellow part of the egg will work as a nice “binder” to make things a bit less dry and gives a bit of extra calories and fat, in case that would not be enough yet 🤣</li>
</ol>
<p>That’s it, once the cooking is over, your Rösti are ready to be eat 🤗</p>
<p>To infinity and beyond 🚀</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[How to close Ionic modals using the hardware back button]]></title>
          <description><![CDATA[How to close Ionic modals using the hardware back button in applications developed with Angular or StencilJS]]></description>
          <link>https://daviddalbusco.com/blog/how-to-close-ionic-modals-using-the-hardware-back-button/</link>
          <pubDate>Mon, 06 May 2019 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*AE_pfiSxN5bMBApg_Nodow.jpeg" alt="" / loading="lazy"></p>
<p>I noticed today that I’m always reusing the exact same piece of code in order to close <a href="https://ionicframework.com" rel="noopener noreferrer external">Ionic</a> modals using the Android hardware back button. Moreover, as I wrote a couple of months ago a blog post about “<a href="https://dev.to/daviddalbusco/how-to-declare-and-use-modals-in-ionic-v4-3nj7" rel="noopener noreferrer external">How to declare and use modals in Ionic v4</a>”, I thought that displaying quickly how I solve this would be a nice add-on.</p>
<p>Actually, I have implemented two different solutions for my applications. One for my <a href="https://angular.io" rel="noopener noreferrer external">Angular</a> applications and one for the [StencilJS] (https://stenciljs.com) one, therefore I’ll showcase both pieces of code 😋</p>
<h3>Disclaimer</h3>
<p>I just tested again to be sure (today is the 6th May 2019), the solution I display using Angular works fine for Android applications but doesn’t for Progressive Web Apps.</p>
<p>Contrariwise the solution I display using StencilJS works like a charm for PWA. According your need, you might use one, the other or “a bit of both” as Star Lord would do 😉</p>
<p>Of course, if you would have a better solution which works for all cases, don’t hesitate to ping as the solutions I’m displaying are implemented in “real” productive applications, I love to learn things and I would be happy to improve them!</p>
<h3>Angular</h3>
<p>Handling the hardware back button using Angular is easy, Ionic does all the job by [dispatching](https://github.com/ionic team/ionic/blob/44fb45e2bcd52580c55cd8a26b396d01c7b53e52/core/src/utils/hardware back-button.ts#L18) an event <code>ionBackButton</code> (which bubbles through the document) when the “physical” button is clicked. We just have to listen to that event and then close our modal if something is happening using the <code>ModalController</code> as we would do, if we would manually close the modal.</p>
<p>Angular also provide a handy <a href="https://angular.io/api/core/HostListener" rel="noopener noreferrer external">decorator</a> <code>HostListener</code> which allow us to listen to events without too much code and without thinking about subscribing and unsubscribing.</p>
<p>In short, here’s my Angular code to close Ionic modals using the hardware back button:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Component} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {ModalController} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@ionic/angular</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    templateUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">my-modal.html</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    styleUrls</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">./my-modal.scss</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">    selector</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-my-modal</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> MyModal</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    constructor</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">private</span><span style="color:#FFB86C;font-style:italic"> modalController</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> ModalController</span><span style="color:#F8F8F2">) {}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    @</span><span style="color:#50FA7B">HostListener</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">document:ionBackButton</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, [</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">$event</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">])</span></span>
<span class="line"><span style="color:#FF79C6">    private</span><span style="color:#FF79C6"> async</span><span style="color:#50FA7B"> overrideHardwareBackAction</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">$event</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> any</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.modalController.</span><span style="color:#50FA7B">dismiss</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><h3>StencilJS</h3>
<p>I mostly, for not saying only, use the Ionic PWA toolkit (= StencilJS) to develop, well I guess you already understand, Progressive Web Apps. As I explained in my introduction, the solution using the event <code>ionBackButton</code> won’t work for such applications, therefore I came up with a workaround which consist of pushing a state in the navigation history when the modal is loaded and popping it when the hardware back button is clicked. Not sure if this solution is ugly or not (don’t judge me on this please if it’s the case) but it seems it works well 🤣</p>
<p>In short, here’s my StencilJS code to close Ionic modals using the hardware back button:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Component, Element, Listen} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@stencil/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    tag</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-my-modal</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    styleUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-my-modal.scss</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> AppMyModal</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    @</span><span style="color:#50FA7B">Element</span><span style="color:#F8F8F2">() el</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLElement</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    async</span><span style="color:#50FA7B"> componentDidLoad</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#F8F8F2">        history.</span><span style="color:#50FA7B">pushState</span><span style="color:#F8F8F2">({modal</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">}, </span><span style="color:#BD93F9">null</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    @</span><span style="color:#50FA7B">Listen</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">window:popstate</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#FF79C6">    async</span><span style="color:#50FA7B"> handleHardwareBackbutton</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">_e</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> PopStateEvent</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">closeModal</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    async</span><span style="color:#50FA7B"> closeModal</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        await</span><span style="color:#F8F8F2"> (</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.el.</span><span style="color:#50FA7B">closest</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">ion-modal</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#FF79C6">              as</span><span style="color:#8BE9FD;font-style:italic"> HTMLIonModalElement</span><span style="color:#F8F8F2">).</span><span style="color:#50FA7B">dismiss</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-header</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">              &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-toolbar</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-buttons</span><span style="color:#50FA7B;font-style:italic"> slot</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">start</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                  &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-button</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">                    onClick</span><span style="color:#FF79C6">={</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">=></span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">closeModal</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">}</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-icon</span><span style="color:#50FA7B;font-style:italic"> name</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">close</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ion-icon</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                  &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ion-button</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ion-buttons</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">              &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ion-toolbar</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ion-header</span><span style="color:#F8F8F2">>,</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-content</span><span style="color:#50FA7B;font-style:italic"> padding</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">                &#x3C;</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">>Yolo&#x3C;/</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">            &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ion-content</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        ];</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><h4>Stencil One</h4>
<p>I didn’t tried yet, but according the [BREAKING CHANGES](https://github.com/ionic team/stencil/blob/core-refactor/BREAKING_CHANGES.md) list of Stencil One, listeners will have to be declared a bit differently in the future. If I get it, we will have to convert the above listener decorator to the following declaration:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Listen</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">popstate</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, { target</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">window</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2"> })</span></span></code></pre><h3>Cherry on the cake 🍒🎂</h3>
<p>Guess what, it works exactly the same with popovers 😉</p>
<p>To infinity and beyond 🚀</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[We are developing an open source editor for presentations]]></title>
          <description><![CDATA[We are developing DeckDeckGo, the open source editor for presentations]]></description>
          <link>https://daviddalbusco.com/blog/we-are-developing-an-open-source-editor-for-presentations/</link>
          <pubDate>Mon, 29 Apr 2019 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*zuqx73hjAWNnmwRpjJ06Zw.jpeg" alt="" / loading="lazy"><br />
<span class="figcaption_hack"><em>Lisbon is a beautiful city. Hopefully, I’m a better engineer than a photographer</em> 😅</span></p>
<p>I’m currently sitting in a plane between Lisbon and Zürich, back from a family trip, and I thought that I could use my travel time to write a non-technical blog post (something new to me) about our project <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>, the open source editor for presentations. In this post I’ll try to summarize who we are, why we are developing this project and where do we stand in the development’s progress.</p>
<p>I’m not sure this post will ever actually interest anyone, but well, I think that at least it will makes my travel faster. Moreover, my mum is currently reading a novel next to me and I also don’t want to disturb her, it’s a win-win situation 😉</p>
<p>I also write these lines because I just finished to read the <a href="https://makebook.io" rel="noopener noreferrer external">MAKE</a> book of <a href="https://twitter.com/levelsio" rel="noopener noreferrer external">Pieter Levels</a> which was really interesting. His “indie” point of view on the startup “world” seems honest and quite refreshing to me. I could definitely advise you to acquire it.</p>
<h3>Introduction</h3>
<p>First thing first, I should probably explain what’s DeckDeckGo 😆</p>
<p><strong>DeckDeckGo aims to be the open source editor for PWA presentations.</strong></p>
<p>What does that mean?</p>
<p>It means that every presentations you will write and publish with DeckDeckGo, are going to be apps (= Progressive Web Apps) too. Basically, we are developing an app to let you write decks of slides which are going to be published online as apps too (I hope that’s “meta” enough for you 😉).</p>
<p>Furthermore, we thought that it would be cool if our project wasn’t “just” an editor but also an online community for sharing presentations, slides and talks about your interests and ideas.</p>
<p><em>The flight attendants is passing by with sandwiches, I’ll continue the article<br />
soon … and I’m back</em></p>
<p>I want also to emphasis something else, which might be given for some, but which is important to us: DeckDeckGo is <strong>open source</strong>. We think that you can’t really hope to have a chance to develop a community without being transparent and without sharing. We also hope that by following this path, we are going to become better programmers.</p>
<h3>Who</h3>
<p>We, <a href="https://twitter.com/nasmattia" rel="noopener noreferrer external">Nicolas Mattia</a> and I, are developing DeckDeckGo. Instead of telling you in a common “biography” way who we are, I thought that trying to summarize the first time we met at the <a href="https://zurich.impacthub.ch" rel="noopener noreferrer external">Impact Hub Zürich</a> (a great co-working space in Zürich) would just speak by itself. Let me try to summarize this very first discussion:</p>
<ul>
<li>Hey, I’m David, I come from the French-speaking area of Switzerland</li>
<li>Hey, I’m Nicolas, I come from the French-speaking part too</li>
</ul>
<p><em>Obviously at that point we switched to french 😜</em></p>
<ul>
<li>No way, I come from Jura</li>
<li>Really, I’m from Neuchâtel, that’s next door</li>
<li>How cool is that. What do you do?</li>
<li>I weird backend stuffs using <a href="https://www.haskell.org" rel="noopener noreferrer external">Haskell</a>, like wizardry, it’s the future. And you?</li>
</ul>
<p><em>Actually he didn’t exactly said that, it’s just…I don’t fully understand the<br />
black magic Nicolas is using 🤣</em></p>
<ul>
<li>I’m a fullstack freelancer/project manager but currently I mostly develop apps<br />
using <a href="https://ionicframework.com" rel="noopener noreferrer external">Ionic</a> and <a href="https://angular.io" rel="noopener noreferrer external">Angular</a></li>
<li>Nice. Let’s build something together one of these days</li>
<li>Definitely</li>
</ul>
<p>Fast forward, Nicolas spent some months abroad working remotely, meanwhile I started DeckDeckGo and finally two months ago, while we were having dinner, I “convinced” him to jump into the project and we started to brainstorm and develop all the new cool ideas we had together 🚀</p>
<h3>Why</h3>
<p>In October 2018 I had the opportunity to give a talk about Web Components at the [Pantalks](https://www.meetup.com/fr-FR/Pantalks-tech-non-tech-talks-Panter-AG Zurich/events/255430094/). When I was preparing my presentation, it hits me that wasn’t actually using the technology I was supposed to demonstrate in order to develop my slides. That’s why I decided to “quickly” build a small new library to support my presentation.</p>
<p>I could have stop there but there is something really interesting with presentations: everyone has written a presentation once in her/his life and everyone is super creative too. Therefore, each times I spoke about “my pet project” with someone, new crazy and awesome ideas were emerging.</p>
<p>From there it was really easy for me to go a bit “out of control” (like I like to say). I just began to develop all these features like being able to remote control any presentations using WebRTC, or being able to add easily charts, or being able to easily add Gifs, Youtube videos or even QR codes and more.</p>
<p>Time passed “but” the project remains a frontend project for techies as, mostly, I didn’t wanted to make alone the (big) step of turning this “small” library into a full product, until fortunately, Nicolas decided to join me 🤟</p>
<h3>Where</h3>
<p>Where do we stand? Well firstly, I’ve to admit that we are maybe a bit behind our very first schedule. First time we discussed about planing, I clearly remember Nicolas telling me something like “Easy, such a backend solution, 2–3 days tops” 😂</p>
<p>For our defense, I’ve to add that since that day we also had a lot of new ideas, which we were agree to develop, as for us, DeckDeckGo is as much a learning tool as a fun project to develop.</p>
<p>Furthermore, we have both other real main activities. I’m a freelancer dedicate to my client’s projects and Nicolas is employee. That let us evenings, late nights, super early mornings and weekends to nerd DeckDeckGo.</p>
<p><em>I have to shutdown the computer, we are about to land … and I’m back. We landed, I said bye to my family and now I’m in the train between Basel and Zürich. Also, welcome back to Switzerland, 12.10 CHF (around 12$) for a ham sandwich and a bottle of water 🙈</em></p>
<p>That being said, we are both aware that “we have to unleash a beta soon” and that we should focus on core features in order to be able to ship soonish a MVP (= minimum viable product). To achieve this goal, we think that the following are the missing features we have still to develop:</p>
<ul>
<li>Publication: collect the user slides, pack everything with the existing starter kit and unleash the decks online as PWAs</li>
<li>Media: we don’t have yet developed the ability to upload images in any slides</li>
<li>Feed: automatically generate the social card and feed layout of each presentations</li>
</ul>
<p>Therefore, without any pressure, our actual goal is to expose DeckDeckGo to its first tests IRL somewhere in June and we hope to start straight afterwards our “beta program” for more extended tests and, hopefully, gather a couple of users’ feedbacks and maybe even better, let the first users write their presentations “for real” 🤞</p>
<h3>Cherry on the cake 🍒🎂</h3>
<p>DeckDeckGo, I guess you get it, is open source, therefore, if you wish to track our progress or wish to already contribute to our “small” pet project, you could find us on <a href="https://github.com/deckgo/deckdeckgo" rel="noopener noreferrer external">GitHub</a> .</p>
<p>Finally, if you would like to help us shape a cool presentation platform and editor by joining our <a href="https://deckdeckgo.com/?index=2" rel="noopener noreferrer external">beta program</a>, that would be awesome. We would love to have you on board and to hear from you, “join the deck side” 😉</p>
<p><em>It’s now 21:25 and I’m finally reaching out Zürich. Not sure someone will ever read these final lines but this article was definitely useful to me, I totally lost the track of the travel time and it just felt like a couple of minutes. Might do the exercise again in the future.</em></p>
<p>To infinity and beyond 🚀</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Hide environment variables in your StencilJS project]]></title>
          <description><![CDATA[How to hide environment variables in the repo of your projects developed with StencilJS or the Ionic PWA Toolkit]]></description>
          <link>https://daviddalbusco.com/blog/hide-environment-variables-in-your-stenciljs-project/</link>
          <pubDate>Wed, 27 Mar 2019 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*uC9a-6ZDkV2PKxYv0QW6Kw.jpeg" alt="" / loading="lazy"></p>
<p>In a previous <a href="https://medium.com/stencil-tricks/environment-variables-with-stenciljs-57e9da591280" rel="noopener noreferrer external">article</a> I described how I implemented environment variables in my <a href="https://stenciljs.com/" rel="noopener noreferrer external">Stencil’s</a> projects or in my projects created with the <a href="https://ionicframework.com/pwa/toolkit" rel="noopener noreferrer external">Ionic PWA toolkit</a>.</p>
<p>Since this publication, we began to work actively on our upcoming editor for PWA presentations, <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>, and as the project is open source and published on <a href="https://github.com/deckgo/deckdeckgo" rel="noopener noreferrer external">GitHub</a>, we thought that it would maybe be a not too bad idea to not publish our API keys and other <a href="https://firebase.google.com" rel="noopener noreferrer external">Firebase</a> tokens online in our public repo 😉</p>
<p>For that reason, I had to go a step further than my previous post respectively I had to find and develop a solution to save separately our keys. Therefore I’m happy to share my solution with this new blog post 😃</p>
<h4>Side note</h4>
<p>Yes, we are aware that our environment variables will be contained in our client bundle and therefore exposed to anyone with a bit of retro engineering but we thought that it would be a bit cleaner to not push them in our Git repo.</p>
<h3>Getting started</h3>
<p>Stencil’s projects relies on <a href="https://rollupjs.org" rel="noopener noreferrer external">Rollup</a>, therefore we are not going to reinvent the wheel, we are just going to use the Rollup’s plugin <a href="https://github.com/rollup/rollup-plugin-replace" rel="noopener noreferrer external">rollup plugin-replace</a> which we install running the following command line:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">npm install rollup</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">plugin</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">replace </span><span style="color:#FF79C6">--</span><span style="color:#F8F8F2">save</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">dev</span></span></code></pre><p>This plugin will allow us to replace values at bundle time.</p>
<h3>Configuring the environments</h3>
<p>Once the plugin installed, we could edit our configuration in our bootstrap classes respectively we could edit our <code>app.ts</code> file in order to replace the values we want to hide. In this specific post we are going to hide the url of the API. To do so, we are going to replace the values with a unique selector (which will be automatically replaced with the real values at bundling time once we have implemented the all solution):</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {setupConfig} </span><span style="color:#FF79C6">from</span></span>
<span class="line"><span style="color:#E9F284">                '</span><span style="color:#F1FA8C">../app/services/environment/environment-config</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">setupConfig</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    url</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">&#x3C;@API_URL@></span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    production</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><p>In my previous article we defined two different environments and therefore I will still assume that our goal is to handle two environments, respectively <code>production</code> and <code>development</code> .</p>
<p>Identically we are then going to replace the API url with our selector in the development configuration, respectively in the <code>app-dev.ts</code> file:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {setupConfig} </span><span style="color:#FF79C6">from</span></span>
<span class="line"><span style="color:#E9F284">                '</span><span style="color:#F1FA8C">../app/services/environment/environment-config</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">setupConfig</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    url</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">&#x3C;@API_URL@></span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    production</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> false</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><p><em>Note that we are using <code>&lt;@</code> and <code>@&gt;</code> to make our selector really unique. These attributes aren’t static, if you rather like to use others it’s absolutely up-to you, no problem. You should then just reflect later in this tutorial your modifications when you will configure the plugin.</em></p>
<h4>Defining the selectors’ values</h4>
<p>To finalize the configuration, we should now create “somewhere” some files which will contain the real values for our selectors. Personally, I have chosen a solution where I handle the values in <code>json</code> files, placed at the root of my project and excluded from Git (I have added them to the list of Git <code>.ignored</code> files). We could therefore for example create a new file <code>config.prod.json</code> at the root of the project and edit it with our selector and real values:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#E9F284">  "</span><span style="color:#F1FA8C">API_URL</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">: </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://api.production.com</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Of course, we should also now create another configuration file <code>config.dev.json</code> for the selector and values or our development environment:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#E9F284">  "</span><span style="color:#F1FA8C">API_URL</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">: </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">http://localhost:3002</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><h3>Running the application</h3>
<p>Now that our configuration is ready, we should just configure and use the plugin we installed before in order to replace the selector with the real values at bundle time. For that purpose we are going to edit <code>stencil.config.ts</code> like the following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> replace </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">rollup-plugin-replace</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">// See my previous article for development detection</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> dev</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> process.argv </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> process.argv.</span><span style="color:#50FA7B">indexOf</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">--dev</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">></span><span style="color:#FF79C6"> -</span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> devConfig </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./config.dev.json</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> prodConfig </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./config.prod.json</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> configValues </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> dev </span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2"> devConfig </span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> prodConfig;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> config</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Config</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    ...</span></span>
<span class="line"><span style="color:#F8F8F2">    plugins: [</span></span>
<span class="line"><span style="color:#50FA7B">      replace</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        exclude</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">node_modules/**</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">        delimiters</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">&#x3C;@</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">@></span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">        values</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> configValues</span></span>
<span class="line"><span style="color:#F8F8F2">      }),</span></span>
<span class="line"><span style="color:#FF79C6">    ...</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Voilà, nothing more, nothing less 🎉 From now on, each time you application will be bundled, the configuration values you saved separately or you didn’t committed in your repo will be injected at bundle time, easy peasy 👻</p>
<h3>Cherry on the cake 🍒🎂</h3>
<p>Like I said in my introduction, this solution is implemented in our upcoming editor for PWA presentations DeckDeckGo and, as this project is open source, you are most welcomed to have a look at the solution I have implemented. You will notice that I implemented the exact same code and configuration as above but you will not find any <code>config.prod.json</code> or <code>config.dev.json</code> in our repo 😉</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">https:</span><span style="color:#6272A4">//github.com/deckgo/deckdeckgo/tree/master/studio</span></span></code></pre><p>To infinity and beyond 🚀</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Environment variables with StencilJS]]></title>
          <description><![CDATA[How to handle environment variables in projects developed with StencilJS or the Ionic PWA Toolkit]]></description>
          <link>https://daviddalbusco.com/blog/environment-variables-with-stenciljs/</link>
          <pubDate>Sun, 03 Mar 2019 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*Q-lN05mJu76_smkG8Qc4aw.jpeg" alt="" / loading="lazy"></p>
<p>I noticed that the question regarding how to handle environment variables in <a href="https://stenciljs.com" rel="noopener noreferrer external">Stencil’s</a> projects or projects created with the <a href="https://ionicframework.com/pwa/toolkit" rel="noopener noreferrer external">Ionic PWA toolkit</a> often pops up 🤔</p>
<p>As I have implemented a solution to handle such parameters in the <a href="https://deckdeckgo.app" rel="noopener noreferrer external">remote control</a> of my project <a href="https://deckdeckgo.com" rel="noopener noreferrer external">DeckDeckGo</a>, the Progressive Web App alternative for simple presentations, I thought about sharing my small implementation in this new article.</p>
<h3>Credits</h3>
<p>The following solution was inspired by the one developed in the Ionic core project. One of the entry point for me was discovering the method <a href="https://github.com/ionic-team/ionic/blob/39fbc323480207417b41ff20d22badd91798ee2d/core/src/utils/config.ts#L182" rel="noopener noreferrer external">setupConfig</a> in their source code. Therefore kudos to the awesome Ionic team ❤️</p>
<h3>Getting started</h3>
<p>The solution described in this tutorial as for goal to handle two environments, a <code>development</code> and a <code>production</code> environments. In each of these we are going to define a variable which points to a different end point url.</p>
<p><em>Note that the example below was developed with the Ionic PWA toolkit.</em></p>
<h3>Configuring the environments</h3>
<p>To begin our implementation, we are going to define an interface which should contains our variable(s) and a setup method which aims to “push” its value in the <code>window</code> object. This means that when our application will start, we are going to call this method in order to define the environment variables which should be use at runtime for the all application.</p>
<p><em>As I display the code of my own project, you might find references to the names <code>DeckDeckGo</code> or its short form <code>DeckGo</code>. Just replace these with the name of your project in your implementation.</em></p>
<p>To implement the interface and function you could for example create a new file called <code>environment-config.tsx</code> :</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#6272A4">// The interface which define the list of variables</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> interface</span><span style="color:#8BE9FD;font-style:italic"> EnvironmentConfig</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    url</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> function</span><span style="color:#50FA7B"> setupConfig</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">config</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> EnvironmentConfig</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">window) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> win </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> window </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> any</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> DeckGo </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> win.DeckGo;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (DeckGo </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> DeckGo.config </span><span style="color:#FF79C6">&#x26;&#x26;</span></span>
<span class="line"><span style="color:#F8F8F2">        DeckGo.config.constructor.name </span><span style="color:#FF79C6">!==</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">Object</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">        console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">DeckDeckGo config was already initialized</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    win.DeckGo </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> win.DeckGo </span><span style="color:#FF79C6">||</span><span style="color:#F8F8F2"> {};</span></span>
<span class="line"><span style="color:#F8F8F2">    win.DeckGo.config </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        ...</span><span style="color:#F8F8F2">win.DeckGo.config,</span></span>
<span class="line"><span style="color:#FF79C6">        ...</span><span style="color:#F8F8F2">config</span></span>
<span class="line"><span style="color:#F8F8F2">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> win.DeckGo.config;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Now that we have created a setup function, we will need to use it when the application start. As our goal is two have two different environments, we are first going to modify the main application class <code>app.ts</code> to be the one which define and use the <code>production</code> environment. We are going to consume the above method we have created and define our url for the production.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {setupConfig} </span><span style="color:#FF79C6">from</span></span>
<span class="line"><span style="color:#E9F284">                '</span><span style="color:#F1FA8C">../app/services/environment/environment-config</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">setupConfig</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    url</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">https://api.production.com</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><p>Then we are going to create a second bootstraping class beside it to be the one which are going to load the <code>development</code> configuration. For that purpose let’s create in addition to the main class a file called <code>app-dev.ts</code> which will contains the following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {setupConfig} </span><span style="color:#FF79C6">from</span></span>
<span class="line"><span style="color:#E9F284">                '</span><span style="color:#F1FA8C">../app/services/environment/environment-config</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#6272A4">// When serve locally: http://localhost:3002</span></span>
<span class="line"><span style="color:#50FA7B">setupConfig</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    url</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> location.protocol </span><span style="color:#FF79C6">+</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">//</span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> +</span><span style="color:#F8F8F2"> location.hostname </span><span style="color:#FF79C6">+</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">:3002</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span></code></pre><h3>Running the application</h3>
<p>Now that we have two different entry points to start our application, we should be able to choose between these while running our command lines. For that purpose we are going, firstly, to modify the configuration file <code>stencil.config.ts</code> in order to make the <code>globalScript</code> property variable.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">let</span><span style="color:#F8F8F2"> globalScript</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">src/global/app.ts</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> dev</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#F8F8F2">           process.argv </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> process.argv.</span><span style="color:#50FA7B">indexOf</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">--dev</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">></span><span style="color:#FF79C6"> -</span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">if</span><span style="color:#F8F8F2"> (dev) {</span></span>
<span class="line"><span style="color:#F8F8F2">    globalScript </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">src/global/app-dev.ts</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> config</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Config</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">     ...</span></span>
<span class="line"><span style="color:#F8F8F2">     globalScript: globalScript,</span></span>
<span class="line"><span style="color:#FF79C6">     ...</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>As you could notice in the above code, the configuration will test a parameter <code>--dev</code> to check if we want to use the <code>development</code> environment or the default one, the <code>production</code> .</p>
<p>To pass that parameter from the command line, we are just going to add a new script to our <code>package.json</code> . Beside <code>npm run start</code> we are going to create a new target <code>npm run dev</code> which aims to start the application for the <code>development</code> environment.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#E9F284">"</span><span style="color:#F1FA8C">scripts</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">: {</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">build</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">stencil build</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">start</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">stencil build --watch --serve</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">, </span><span style="color:#6272A4">// Production</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">dev</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">stencil build --dev --watch --serve</span><span style="color:#E9F284">"</span><span style="color:#6272A4"> // Development</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><h3>Reading the variables</h3>
<p>Now that we have set up the configuration and the scripts to switch between both environments we have only one final piece to implement, the one regarding actually reading the values, in our example, reading the value of our url.</p>
<p>For that purpose I suggest to create a singleton which aims to load the configurations parameters in memory once and to expose a <code>get</code> method which should allow us to query specific variables (as we may have more than one environments variables 😉). We could create that new service in a new separate file called <code>environment-config.service.tsx</code> :</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {EnvironmentConfig} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./environment-config</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> EnvironmentConfigService</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    private</span><span style="color:#FF79C6"> static</span><span style="color:#F8F8F2"> instance</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> EnvironmentConfigService</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    private</span><span style="color:#F8F8F2"> m</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Map</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">keyof</span><span style="color:#FFB86C;font-style:italic"> EnvironmentConfig</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">any</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    private</span><span style="color:#FF79C6"> constructor</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#6272A4">        // Private constructor, singleton</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">init</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    static</span><span style="color:#50FA7B"> getInstance</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">EnvironmentConfigService.instance) {</span></span>
<span class="line"><span style="color:#F8F8F2">            EnvironmentConfigService.instance </span><span style="color:#FF79C6">=</span></span>
<span class="line"><span style="color:#FF79C6;font-weight:bold">                              new</span><span style="color:#50FA7B"> EnvironmentConfigService</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> EnvironmentConfigService.instance;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    private</span><span style="color:#50FA7B"> init</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">window) {</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> win </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> window </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> any</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> DeckGo </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> win.DeckGo;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">        this</span><span style="color:#F8F8F2">.m </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Map</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">keyof</span><span style="color:#FFB86C;font-style:italic"> EnvironmentConfig</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">any</span><span style="color:#F8F8F2">>(Object.</span><span style="color:#50FA7B">entries</span><span style="color:#F8F8F2">(DeckGo.config) </span><span style="color:#FF79C6">as</span><span style="color:#8BE9FD;font-style:italic"> any</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    get</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">key</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6"> keyof</span><span style="color:#8BE9FD;font-style:italic"> EnvironmentConfig</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">fallback</span><span style="color:#FF79C6">?:</span><span style="color:#8BE9FD;font-style:italic"> any</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> any</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> value </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.m.</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(key);</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#F8F8F2"> (value </span><span style="color:#FF79C6">!==</span><span style="color:#BD93F9"> undefined</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2"> value </span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> fallback;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>That’s it, that was the last piece needed to implement environment variables in a Stencil project or an Ionic PWA toolkit application 🎉</p>
<p>To get a variable, you could now simply call anywhere in your code your service and ask for the value of a parameter, like in the following example:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> url</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#F8F8F2">                EnvironmentConfigService.</span><span style="color:#50FA7B">getInstance</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">url</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">My environment variable value:</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, url);</span></span></code></pre><h3>Cherry on the cake 🍒🎂</h3>
<p>Like I said in my introduction, this solution is implemented in the remote control of my project DeckDeckGo, and guess what, this project is open source. Therefore, if you would like to checkout a concrete example of such implementation, you could browse or clone the DeckDeckGo repository 😃</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">git clone https:</span><span style="color:#6272A4">//github.com/deckgo/deckdeckgo</span></span></code></pre><p>To infinity and beyond 🚀</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Add Web Push Notifications to your Ionic PWA]]></title>
          <description><![CDATA[Add Web Push Notifications to your Ionic Angular PWA using Google FCM]]></description>
          <link>https://daviddalbusco.com/blog/add-web-push-notifications-to-your-ionic-pwa/</link>
          <pubDate>Fri, 22 Feb 2019 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*zZyMLQSqP8VZsi-ptOgLSw.jpeg" alt="" / loading="lazy"></p>
<p>I volunteered to showcase to our <a href="https://www.meetup.com/fr-FR/Ionic-Zurich/" rel="noopener noreferrer external">Ionic Zürich Meetup</a> community the implementation of Web Push Notifications, using <a href="https://firebase.google.com/docs/cloud-messaging/" rel="noopener noreferrer external">Google FCM</a>, in a Progressive Web App developed with <a href="https://ionicframework.com/" rel="noopener noreferrer external">Ionic </a> and <a href="https://angular.io/" rel="noopener noreferrer external">Angular</a>. Although the <a href="https://firebase.google.com/docs/cloud-messaging/js/client" rel="noopener noreferrer external">set up guide</a> provided by Google is relatively clear, I thought it might be a good idea to write a new article, in form of a step by step tutorial, about that particular topic (specially to help me not forget something 😅).</p>
<p><em>For the purpose of this article I assume that you already have an <code>@ionic/angular</code> application. If not, you could start a new project running the following command in a terminal:</em></p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">ionic start </span><span style="color:#FF79C6">--</span><span style="color:#F8F8F2">type</span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2">angular</span></span></code></pre><p><em>I will also assume that your application is already a Progressive Web App, if<br />
not have a look to the Angular <a href="https://angular.io/guide/service-worker-getting-started" rel="noopener noreferrer external">documentation</a> to get to know how to add that support to your project or run the following command in your freshly created new Ionic project:</em></p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">ng add @</span><span style="color:#50FA7B;font-style:italic">angular</span><span style="color:#FF79C6">/</span><span style="color:#50FA7B;font-style:italic">pwa</span><span style="color:#F8F8F2"> — project app</span></span></code></pre><h3>Spoiler alert ⚠️</h3>
<p>Let’s make that straight clear from the begin, Web Push Notifications, at least when I’m writing these lines the 16th February 2019, <a href="https://caniuse.com/#search=notification" rel="noopener noreferrer external">isn’t supported by iOS</a>.</p>
<p>Furthermore, as we are looking to implement Web Push Notifications in a Progressive Web App, we might be interested to push notifications to a mobile devices. Therefore, it’s also worth to notice, that as far as I tested recently, such notifications will not be triggered on the devices if the application is completely closed on Android devices. Regardless of the device status, lock or unlocked, notifications will only be triggered (“will ring the bell”) as long as the application is in foreground or background, but it might deserve further tries.</p>
<p>That being said, let’s start by setting up Firebase and its Cloud Messaging solution.</p>
<h3>Configure Firebase in your project</h3>
<p>If you are already using Google Firebase in your project, skip this chapter. If you don’t, first add the firebase libraries to your Angular application using the following command line:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">npm install firebase </span><span style="color:#FF79C6">--</span><span style="color:#F8F8F2">save</span></span></code></pre><p>Once done, go to you Firebase console to retrieve your configuration and copy the properties.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*pY72dn5n0tPtKvuCEqlWRQ.png" alt="" / loading="lazy"><br />
<span class="figcaption_hack">Click the “&lt;/&gt;” button to discover your configuration</span></p>
<p><img src="https://daviddalbusco.com/assets/images/1*sHKB6IeYW956WXKj_k_2Hw.png" alt="" / loading="lazy"><br />
<span class="figcaption_hack">Copy the JSON properties</span></p>
<p>Once copied, add these information to the environment of your project, for example in your <code>environment.ts</code> file.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> environment </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">  production</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> false</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  firebase</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    apiKey</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">API_KEY</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    authDomain</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">AUTH_DOMAIN</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    databaseURL</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">https://PROJECT.firebaseio.com</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    projectId</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">PROJECT_ID</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    storageBucket</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">PROJECT.appspot.com</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    messagingSenderId</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">MSG_SENDER_ID</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Finally initialize Firebase in your application’s startup, for example in your <code>app.component.ts</code> file.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {firebase} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@firebase/app</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {environment} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">../environments/environment</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">async </span><span style="color:#50FA7B">ngOnInit</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#F8F8F2">    firebase.</span><span style="color:#50FA7B">initializeApp</span><span style="color:#F8F8F2">(environment.firebase);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p><em>Note: pay attention to the import of Firebase. If you are using an editor which add automatically the import, it might resolves <em><code>import * as firebase from 'firebase'</code></em> instead of <em><code>import {firebase} from '@firebase/app'</code></em> which would lead to loading all the libraries, which obviously we don’t want. Furthermore, if you do so, the library itself will displays a warning in the console to ask you to correct the import for production.</em></p>
<h3>Configure Google Fire Cloud Messaging</h3>
<p>In order to be able to push Web Push Notifications with FCM, you will need a Web Credential called “Voluntary Application Server Identification” or “VAPID” key. To tell your application to subscribe to the notifications, you will need to associate such a key with your project.</p>
<p>To generate a new key, open the [Cloud Messaging] (https://console.firebase.google.com/project/_/settings/cloudmessaging/) tab of your Firebase console <strong>Settings</strong> pane and scroll to the <strong>Web configuration</strong> section.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*vIPfMFcKbvEUYIXZw84c4g.png" alt="" / loading="lazy"><br />
<span class="figcaption_hack">Generate a VAPID key pair in your Firebase Console (Tab “Settings &gt; Cloud<br />
Messaging”)</span></p>
<h3>Configure your PWA for FCM</h3>
<p>Once the previous VAPID key generated, you will have to add it to your project. I would suggest to add it to the other Firebase properties respectively to your file <code>environment.ts</code> file like the following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> const</span><span style="color:#F8F8F2"> environment </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">    ...</span></span>
<span class="line"><span style="color:#F8F8F2">    firebase: {</span></span>
<span class="line"><span style="color:#FF79C6">        ...</span></span>
<span class="line"><span style="color:#F8F8F2">        vapidKey: </span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">VAPID_KEY_WE_GENERATED</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">};</span></span></code></pre><p>Finally, to authorize Google FCM to send messages to your application, you will need to add first a <strong>fixed</strong> value to the Web App Manifest (<code>manifest.json</code> or other <code>manifest.webmanifest</code>) file of your project.</p>
<p><em>Yes this value is the same for every single apps in the world, it identifies Google FCM as sender of the notifications.</em></p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">{</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">gcm_sender_id</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">: </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">103953800507</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><h3><strong>Before coding</strong></h3>
<p>As we are implementing this solution in a Progressive Web App, before beginning to code, I think it’s important to briefly summarize which layer of your application will be responsible for doing what. It may sound clear for you but I’m not ashamed to admit that it took me a while to get it 😅</p>
<h4>Service Worker</h4>
<p>The Service Worker is responsible to receive or listen to messages when the application is in the background or close. Therefore we have to initialize Firebase in a Service Worker script and listen for new messages.</p>
<p>It’s also worth to notice, if I understood correctly, that we have to reference the Firebase libraries in the Service Worker scripts as the worker is separated of the bundle and therefore don’t have access to the vendor libraries packaged in your application.</p>
<h4>Application</h4>
<p>The application, “your Angular code”, is in charge of registering the VAPID key and asking the users for their permissions to send notifications or not. These two goals are a must to achieve our goal.</p>
<p>If you want to go deeper, then you could also add to your application layer a listener to receive new messages, in case you would like to display them differently when the application is in the foreground, and you would have to take care of saving and refreshing the users tokens too.</p>
<h3>Hands-on</h3>
<p>Let’s begin the implementation with the Service Worker layer followed by the application itself.</p>
<h4>Service Worker</h4>
<p>As described in the Firebase <a href="https://firebase.google.com/docs/cloud-messaging/js/receive" rel="noopener noreferrer external">documentation</a> you should create a new <code>firebase-messaging-sw.js</code> in your <code>src</code> folder. I would suggest not to use another name as, I think, Firebase is also explicitly looking for that filename when subscribing for new messages.</p>
<p>Before implementing its content, you will have first to add this file to the list of <code>assets</code> of your <code>angular.json</code> otherwise it will not be bundled with your application.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#E9F284">"</span><span style="color:#F1FA8C">assets</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">: [</span></span>
<span class="line"><span style="color:#E9F284">     "</span><span style="color:#F1FA8C">src/firebase-messaging-sw.js</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">]</span></span></code></pre><p>Once done, you could now add its implementation. As you could notice, nothing crazy, we just load the Firebase scripts for the Service Worker, initialize and add a listener for the messages, that’s it.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">importScripts</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">https://www.gstatic.com/firebasejs/5.7.3/firebase-app.js</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#50FA7B">importScripts</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">https://www.gstatic.com/firebasejs/5.7.3/firebase-messaging.js</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">firebase.</span><span style="color:#50FA7B">initializeApp</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    messagingSenderId</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">YOUR_SENDER_ID</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> messaging </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> firebase.</span><span style="color:#50FA7B">messaging</span><span style="color:#F8F8F2">();</span></span></code></pre><p>To find your sender ID, you could go back to your Firebase console, you should find it again under the Tab “Settings &gt; Cloud Messaging”.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*FJNN0xRb3HVwqfTT8Va-1w.png" alt="" / loading="lazy"><br />
<span class="figcaption_hack">Find your Firebase Sender ID (Tab Tab “Settings &gt; Cloud Messaging”)</span></p>
<h3>Application</h3>
<p>For a relatively clean implementation, I suggest to create a new service to implement the features.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">ionic g service notifications</span></span></code></pre><h4>Initialization</h4>
<p>Here is maybe the tricky part of the solution. Actually it isn’t really tricky, it is just one line of code but it took me way to long to solve it. As we are building a PWA using Ionic and Angular, we are already most probably using <code>@angular/pwa</code> which takes care at bundle time to generate dynamically a Service Worker for your application. Therefore you could ask your self, at least I did, how to concatenate this dynamic worker with the worker we just created, the <code>firebase-messaging-sw.js</code> file? I found different solutions but I think that the easiest one is just to register the worker using the method <code>userServiceWorker()</code> provided by Firebase. This function will take care of adding your Web Push Notifications Service Worker at runtime to the worker of your application. Worth to notice that this has to be done when the Service Worker is ready.</p>
<p>Furthermore, as explained above, the other important part of the solution is the initialization of the VAPID key.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Injectable} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {firebase} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@firebase/app</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@firebase/messaging</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {environment} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">../environments/environment</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Injectable</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    providedIn</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">root</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> NotificationsService</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">init</span><span style="color:#F8F8F2">()</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">void</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">void</span><span style="color:#F8F8F2">>((</span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#F8F8F2">, </span><span style="color:#FFB86C;font-style:italic">reject</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">        navigator.serviceWorker.ready.</span><span style="color:#50FA7B">then</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">registration</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#6272A4">            // Don't crash an error if messaging not supported</span></span>
<span class="line"><span style="color:#FF79C6">            if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">firebase.messaging.</span><span style="color:#50FA7B">isSupported</span><span style="color:#F8F8F2">()) {</span></span>
<span class="line"><span style="color:#50FA7B">                   resolve</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">                   return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">            const</span><span style="color:#F8F8F2"> messaging </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> firebase.</span><span style="color:#50FA7B">messaging</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">            // Register the Service Worker</span></span>
<span class="line"><span style="color:#F8F8F2">            messaging.</span><span style="color:#50FA7B">useServiceWorker</span><span style="color:#F8F8F2">(registration);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">            // Initialize your VAPI key</span></span>
<span class="line"><span style="color:#F8F8F2">            messaging.</span><span style="color:#50FA7B">usePublicVapidKey</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#F8F8F2">                  environment.firebase.vapidKey</span></span>
<span class="line"><span style="color:#F8F8F2">            );</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">            // Optional and not covered in the article</span></span>
<span class="line"><span style="color:#6272A4">            // Listen to messages when your app is in the foreground</span></span>
<span class="line"><span style="color:#F8F8F2">            messaging.</span><span style="color:#50FA7B">onMessage</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">payload</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">                console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(payload);</span></span>
<span class="line"><span style="color:#F8F8F2">            });</span></span>
<span class="line"><span style="color:#6272A4">            // Optional and not covered in the article</span></span>
<span class="line"><span style="color:#6272A4">            // Handle token refresh</span></span>
<span class="line"><span style="color:#F8F8F2">            messaging.</span><span style="color:#50FA7B">onTokenRefresh</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">                messaging.</span><span style="color:#50FA7B">getToken</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">then</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#F8F8F2">                (</span><span style="color:#FFB86C;font-style:italic">refreshedToken</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">                    console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(refreshedToken);</span></span>
<span class="line"><span style="color:#F8F8F2">                }).</span><span style="color:#50FA7B">catch</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">err</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">                    console.</span><span style="color:#50FA7B">error</span><span style="color:#F8F8F2">(err);</span></span>
<span class="line"><span style="color:#F8F8F2">                });</span></span>
<span class="line"><span style="color:#F8F8F2">            });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">            resolve</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">        }, (</span><span style="color:#FFB86C;font-style:italic">err</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">            reject</span><span style="color:#F8F8F2">(err);</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>The above method is an initialization function and therefore I suggest to consume it when your application is starting just after the initialization of Firebase in your <code>app.component.ts</code> file:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">async </span><span style="color:#50FA7B">ngOnInit</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#F8F8F2">    firebase.</span><span style="color:#50FA7B">initializeApp</span><span style="color:#F8F8F2">(environment.firebase);</span></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.notificationsService.</span><span style="color:#50FA7B">init</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><h4>Request permissions</h4>
<p>Obviously you can’t push notifications to a user who would not like to receive such messages. To request his/her agreement you will have then to implement a method which takes care of requesting such permissions. Once a user would have granted them, you will be able to retrieve his/her unique token which you could use later to send the notifications. To the above service you will have to add the following method for that purpose.</p>
<p>In addition, as <a href="https://caniuse.com/#search=notifications" rel="noopener noreferrer external">Web Notifications</a> or FCM messaging aren’t supported by all browsers, the method should contains a couple of tests in order to requests the permissions only if it’s possible.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">requestPermission</span><span style="color:#F8F8F2">(): </span><span style="color:#8BE9FD;font-style:italic">Promise</span><span style="color:#FF79C6">&#x3C;void></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    return new </span><span style="color:#50FA7B">Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">void</span><span style="color:#F8F8F2">>(</span><span style="color:#FFB86C;font-style:italic">async</span><span style="color:#F8F8F2"> (</span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#50FA7B">        if</span><span style="color:#F8F8F2"> (!</span><span style="color:#FFB86C;font-style:italic">Notification</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#50FA7B">            resolve</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#50FA7B">        if</span><span style="color:#F8F8F2"> (!firebase.messaging.isSupported()) {</span></span>
<span class="line"><span style="color:#50FA7B">            resolve</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">            return</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">        try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">            const</span><span style="color:#F8F8F2"> messaging </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> firebase.</span><span style="color:#50FA7B">messaging</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">            await</span><span style="color:#8BE9FD;font-style:italic"> messaging</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">requestPermission</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">            const</span><span style="color:#F8F8F2"> token</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> messaging.</span><span style="color:#50FA7B">getToken</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">            console</span><span style="color:#F8F8F2">.</span><span style="color:#8BE9FD;font-style:italic">log</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">User notifications token:</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">token</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">        } catch (err) {</span></span>
<span class="line"><span style="color:#6272A4">            // No notifications granted</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">        resolve</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>The above method is an initialization function which need a user interaction and therefore I suggest to consume it when your application is started, for example calling it after the initialization in your <code>app.component.ts</code> :</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">ngAfterViewInit</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">     this</span><span style="color:#F8F8F2">.platform.</span><span style="color:#50FA7B">ready</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">then</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">async</span><span style="color:#F8F8F2"> () </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">        await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.notificationsService.</span><span style="color:#50FA7B">requestPermission</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">     });</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>That’s it, we have registered a Service Worker listener and initialized all we need in the application layer respectively you implemented the very basic needed to implement a Web Push Notifications solution using Google Firebase Cloud Messaging in a PWA build with Ionic and Angular 🎉</p>
<h3>Build and deploy</h3>
<p>If I understand correctly the solution we have implemented will only work if serve over https, that’s why we are going to build the application and deploys to a web server.</p>
<p>To build the application, run the following command in your terminal:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">ionic build </span><span style="color:#FF79C6">--</span><span style="color:#F8F8F2">prod</span></span></code></pre><p><em>Note that we are running the command with the option <em><code>--prod</code></em> as per default the service worker will only be included in your application by the Angular CLI in case of that production build.</em></p>
<p>Once built, if you already have your hosting solution, just drop the content of the <code>www</code> folder of your application on your web server. If not, I would then suggest you to use the [Google Firebase Hosting] (https://console.firebase.google.com/project/web-push-notifications-medium/hosting) solution as we are already using their solution for the purpose of this tutorial. If you never used before such a solution, just go back to your Firebase console and click “Get started” in the menu “Hosting”.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*z56WG_qUcxg_LMfJN7--CQ.png" alt="" / loading="lazy"><br />
<span class="figcaption_hack">Initialize a Google Firebase Hosting</span></p>
<p>I will not cover further all the steps to initialize and deploy your application to a Firebase hosting as Google do a great job at guiding you though these steps, therefore, I assume that if you are reaching that point you have now deployed your application to a web server which serve your application though https.</p>
<h3>Test locally</h3>
<p>Now that your application is up and running, you could access it through the url provided by Firebase or your own hosting url. If everything works according plan, just after you accessed your application, it should ask you if you wish or not to receive notifications.</p>
<p><em>Worth to notice, if you are using Google Chrome, don’t try out your application with a private tab or private window as Chrome block these kind of notifications in such tabs or windows. Just use a regular tab or window.</em></p>
<p><img src="https://daviddalbusco.com/assets/images/1*7rDw7XpG4xF2UCp0e6C0hA.png" alt="" / loading="lazy"><br />
<span class="figcaption_hack">Do you want to receive notifications?</span></p>
<p>Of course please, for the purpose of this tutorial authorize the notifications 😉 Once granted, open your browser’s debug console to find out your user’s notification token (in the above method <code>requestPermission</code> you have added a <code>console.log</code> to retrieve this information).</p>
<p><img src="https://daviddalbusco.com/assets/images/1*M2w1xm9QC6NaHaCggN2t7A.png" alt="" / loading="lazy"><br />
<span class="figcaption_hack">Retrieve the user’s notification token</span></p>
<p>Now that you know to whom we are going to send a notification, we could prepare a notification. Google Firebase Cloud Messaging support <a href="https://firebase.google.com/docs/cloud-messaging/send-message" rel="noopener noreferrer external">message requests thought http</a>, meaning that we could send an http request to a Google API server containing the message and its payload which will then be delivered to the user we are specifying in the message.</p>
<p>For the purpose of this tutorial we are going to use cURL and we are going to send the following request:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">curl </span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">X POST </span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">H </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Authorization: key=SERVER_KEY</span><span style="color:#E9F284">"</span><span style="color:#FF79C6"> -</span><span style="color:#F8F8F2">H </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">Content-Type: application/json</span><span style="color:#E9F284">"</span><span style="color:#FF79C6"> -</span><span style="color:#F8F8F2">d </span><span style="color:#E9F284">'</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">{</span></span>
<span class="line"><span style="color:#E9F284">"</span><span style="color:#F1FA8C">notification</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">: {</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">title</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Web Push Notifications</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">body</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">Hey, Hello World</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E9F284">    "</span><span style="color:#F1FA8C">click_action</span><span style="color:#E9F284">"</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> "</span><span style="color:#F1FA8C">https://mywebsite.ccom</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">},</span></span>
<span class="line"><span style="color:#E9F284">"</span><span style="color:#F1FA8C">to</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">: </span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">USER_TOKEN</span><span style="color:#E9F284">"</span></span>
<span class="line"><span style="color:#F8F8F2">}</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C"> "https://fcm.googleapis.com/fcm/send</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">"</span></span></code></pre><p>The request will contain a payload in form of a title, body and an action for the click on the notifications. It will contains the user’s token, which we retrieved above, and will also need the server key, as the message has to be authenticated. To retrieve that key, go back to your Firebase console under tab “<a href="https://console.firebase.google.com/project/web-push-notifications-medium/settings/cloudmessaging/" rel="noopener noreferrer external">Cloud<br />
Messaging</a>”:</p>
<p><img src="https://daviddalbusco.com/assets/images/1*EOsY6NlSfDaoRtydBb_XEw.png" alt="" / loading="lazy"><br />
<span class="figcaption_hack">Find your server key</span></p>
<p>Once you have replaced the user’s token and server key in the above message, remove its line break respectively join the lines of the message in order to have one single line and execute as a command in a terminal and the magic should finally happens 🎉</p>
<p><img src="https://daviddalbusco.com/assets/images/1*c3Jd8c2H9l2UXA83ARctrA.png" alt="" / loading="lazy"></p>
<p><em>Worth to notice, if your application is active or in foreground, at least when I tested on my Macbook OSX Mojave, no notification will pop up. For the purpose of this test, I suggest you to reduce your browser windows.</em></p>
<h3>Test on mobile devices</h3>
<p>Finally we are reaching the last test of this tutorial 😅 As we are looking to implement Web Push Notifications in a Progressive Web App and as we have deployed your application on a web server, it’s time to test it on your mobile device.</p>
<p>To reproduce what would a user would do, I suggest to access the PWA, accept the notifications and then add the application to the home screen.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*8dSbEqS6YeCdvntWYdaZDw.jpeg" alt="" / loading="lazy"><br />
<span class="figcaption_hack">“Install” the PWA on your phone and accept the notifications</span></p>
<p>Now that the application is installed, we have to retrieve the user’s token. Again we are going to open the browser’s debug to find this in the console output.</p>
<p><em>If you never done that before, pair your phone to your computer with a cable an open the “Remote devices” tab in Google Chrome.</em></p>
<p><img src="https://daviddalbusco.com/assets/images/1*j6jT3YwhI7k5bIH_LPQ-zA.png" alt="" / loading="lazy"><br />
<span class="figcaption_hack">Open “Remote devices”</span></p>
<p><img src="https://daviddalbusco.com/assets/images/1*pz3ynVE1mQ8d81V9yZvCrg.png" alt="" / loading="lazy"><br />
<span class="figcaption_hack">Find the users’ token</span></p>
<p>Finally, to run the final test, I suggest you to lock your phone and once done to run again a cURL request in your command line to target this new user’s token. If everything goes well you should have now received a Web Push Notifications which should have ring the bell of your phone and wish should appears on your lock screen 🎉</p>
<p><img src="https://daviddalbusco.com/assets/images/1*1wKpJSGaVIypHx2Ws7BsCw.jpeg" alt="" / loading="lazy"><br />
<span class="figcaption_hack">The Web Push Notifications shimming on my lock screen</span></p>
<h3>Summary</h3>
<p>I hope that you are still here to read these last lines as I kind of feel that this solution and tutorial needed probably too much steps. On the other hand, when we think about it, the solution was relatively easy to implement and it’s kind of cool to notice that it is possible to send push notifications through the web and I’m definitely looking forward to the day where this standard will be implemented on every platforms and browsers.</p>
<p>To infinity and beyond 🚀</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Create a Web Component to keep track of your input's draft with StencilJS]]></title>
          <description><![CDATA[Create a Web Component to keep track of your input's draft with StencilJS]]></description>
          <link>https://daviddalbusco.com/blog/create-a-web-component-to-keep-track-of-your-inputs-draft-with-stenciljs/</link>
          <pubDate>Thu, 24 Jan 2019 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*Kw2TcCGoU1-7Ex8RbyjB0A.jpeg" alt="" / loading="lazy"><br />
<span class="figcaption_hack">Grab a coffee or tea, open a terminal and let’s code a clever <textarea/> with StencilJS</span></p>
<p>Earlier this week, I saw a tweet of <a href="https://medium.com/@ali07cat07" rel="noopener noreferrer external">Ali Spittel</a> who was trying to figure out how <a href="https://github.com/" rel="noopener noreferrer external">GitHub</a>, <a href="https://twitter.com/" rel="noopener noreferrer external">Twitter</a> and others, were able to keep track of the draft of your inputs respectively how such platforms were saving and restoring the content of your <code>input</code> or <code>textarea</code> before you would actually submit them and if the browser would refresh.</p>
<p>Long story short, <a href="https://twitter.com/MrTurnerj" rel="noopener noreferrer external">James Turner</a> is clever and had a look to the minified source code and discovered that your inputs are saved in the session storage of your browser quickly before its refresh and are loaded once the page is displayed again.</p>
<p>I thought that this discovery was really interesting, specially because I always assumed that this was a browser feature and not something which needed implementation, but also because I thought that this could be an interesting use case for a new <a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components" rel="noopener noreferrer external">Web Component</a> compatible with any modern frameworks, or even without, and that’s why I’m writing this article.</p>
<p>_For the purpose of this tutorial, I selected <em><a href="https://stenciljs.com/" rel="noopener noreferrer external">StencilJS</a></em> as a compiler, mostly because I’m a bit more experienced with it, as I developed my pet project _<a href="https://deckdeckgo.com/" rel="noopener noreferrer external">DeckDeckGo</a>* with, but also, to be truly honest, just because I’m in <em>❤️</em> with Stencil *😉</p>
<h3>Let’s get started</h3>
<p>To get started we are going to initialize a new project, a new Web Component. Using a command line, run <code>npm init stencil</code> , pick <code>component</code> for the starter type and enter a name for the project (I used <code>clever-textarea</code> for the purpose of this tutorial).</p>
<p><img src="https://daviddalbusco.com/assets/images/1*foHSCS1AgJ46b-O6WaAG3Q.png" alt="" / loading="lazy"><br />
<span class="figcaption_hack">npm init stencil</span></p>
<p><img src="https://daviddalbusco.com/assets/images/1*h0jM5WWL7H028tgPwhBfSg.png" alt="" / loading="lazy"><br />
<span class="figcaption_hack">select the starter “component”</span></p>
<p><img src="https://daviddalbusco.com/assets/images/1*HLy-80fohxuB3s3tY06zSA.png" alt="" / loading="lazy"><br />
<span class="figcaption_hack">enter a project name</span></p>
<p>That’s it, our new project is initialized. We could now jump into the newly created folder, <code>cd clever-textarea</code> , and start the component runing the command line<code>npm run start</code> in order to test if everything is alright by opening a browser and accessing the url <code>http://localhost:3333</code> 😎</p>
<p><img src="https://daviddalbusco.com/assets/images/1*Jxu5c2jSNxJwAxkIopNGCw.png" alt="" / loading="lazy"><br />
<span class="figcaption_hack">start the local server for the component</span></p>
<p><img src="https://daviddalbusco.com/assets/images/1*3shvGjyXaBw_9kXByxv5sg.png" alt="" / loading="lazy"><br />
<span class="figcaption_hack">access the component</span></p>
<p><em>To continue this tutorial, I suggest you to keep the component running, Stencil will automatically pick the changes we are going to make to the code and will trigger a refresh of the browser on new build.</em></p>
<h3>Let’s code</h3>
<p>We are now all set, we could begin to code our component 🚀 As we want to develop a clever <code>textarea</code>, I suggest that we begin first by removing the default demo code from the component with the goal to just render a dummy blank <code>textarea</code>. For that purpose, we are going to edit the file <code>src/components/my-component/my-component.tsx</code> as Stencil components are built using <a href="https://reactjs.org/docs/introducing-jsx.html" rel="noopener noreferrer external">JSX</a> and Typescript.</p>
<p>_Per default, the component’s name and namespace are set to <em><code>my-component</code></em> respectively _<code>mycomponent</code>* . For simplicity reason, I’ll stick to these names for this article. If you would create a component you would use in a real project, I advise you to rename these informations *😉</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Component} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@stencil/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  tag</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">my-component</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  styleUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">my-component.css</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  shadow</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> MyComponent</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FF79C6">textarea</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">textarea</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>The above code <code>render()</code> a <code>textarea</code> which should update our browser as the following:</p>
<p><img src="https://daviddalbusco.com/assets/images/1*ie9v5mt8r9dZCAmo7FLP2w.png" alt="" / loading="lazy"></p>
<h4>Saving your inputs before refresh</h4>
<p>As James Turner discovered, the trick consists of saving your inputs before the browser would refresh. To do so we could hook the <code>window</code> event <code>beforeunload</code> which we are going to declare once our component is loaded, respectively in one of the <a href="https://stenciljs.com/docs/component-lifecycle" rel="noopener noreferrer external">lifecycles</a> provided by Stencil.</p>
<p>Furthermore to this, in order to retrieve the current value of our <code>textarea</code>, we could use the Stencil’s reference <code>Element</code> to perform a query on the DOM elements of the host and save its value in the <code>sessionStorage</code>.</p>
<p><em>Per default, see your component definition <em><code>@Component</code></em> , the component is going to be shadowed, that’s why we are going to use the selector <em><code>shadowRoot</code></em> in our query.</em></p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Component, Element} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@stencil/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  tag</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">my-component</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  styleUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">my-component.css</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  shadow</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> MyComponent</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  @</span><span style="color:#50FA7B">Element</span><span style="color:#F8F8F2">() el</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLElement</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  componentDidLoad</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> textarea </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.el.shadowRoot.</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">textarea</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#6272A4">    // Save input value before refresh</span></span>
<span class="line"><span style="color:#F8F8F2">    window.</span><span style="color:#50FA7B">addEventListener</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">beforeunload</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,  (</span><span style="color:#FFB86C;font-style:italic">_event</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">      if</span><span style="color:#F8F8F2"> (textarea) {</span></span>
<span class="line"><span style="color:#F8F8F2">        sessionStorage.</span><span style="color:#50FA7B">setItem</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#E9F284">           '</span><span style="color:#F1FA8C">clever-textarea-value</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, textarea.value</span></span>
<span class="line"><span style="color:#F8F8F2">        );</span></span>
<span class="line"><span style="color:#F8F8F2">      }</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FF79C6">textarea</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">textarea</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Once implemented, you could go back to your browser and have a try. Don’t forget to enter a value in your <code>textarea</code>, perform a browser refresh and observe your session storage, you should now find the value your previously entered.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*ss05v5GOv0e5B8D5UdrqxA.png" alt="" / loading="lazy"><br />
<span class="figcaption_hack">enter a value in the textarea and refresh the browser</span></p>
<p><img src="https://daviddalbusco.com/assets/images/1*Mn2nx4e8_gLpyoP67PVOcg.png" alt="" / loading="lazy"><br />
<span class="figcaption_hack">open the debugger and find your value in the session storage</span></p>
<h4>Loading your inputs after refresh</h4>
<p>If you are still here, I hope so, and have tested the above steps by yourself, I guess you already know what’s coming next and how to code it 😅</p>
<p>Now that we have save our inputs when the browser refresh, we could hook the loading of the page, retrieve our value from the <code>sessionStorage</code> and display it. As previously, we are going to use the same Stencil lifecycle to perform this operation and to use again the element reference to manipulate the DOM.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Component, Element} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@stencil/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  tag</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">my-component</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  styleUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">my-component.css</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  shadow</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> MyComponent</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  @</span><span style="color:#50FA7B">Element</span><span style="color:#F8F8F2">() el</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLElement</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  componentDidLoad</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> textarea </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.el.shadowRoot.</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">textarea</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // Save input value before refresh</span></span>
<span class="line"><span style="color:#F8F8F2">    window.</span><span style="color:#50FA7B">addEventListener</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">beforeunload</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,  (</span><span style="color:#FFB86C;font-style:italic">_event</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">      if</span><span style="color:#F8F8F2"> (textarea) {</span></span>
<span class="line"><span style="color:#F8F8F2">        sessionStorage.</span><span style="color:#50FA7B">setItem</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#E9F284">          '</span><span style="color:#F1FA8C">clever-textarea-value</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, textarea.value</span></span>
<span class="line"><span style="color:#F8F8F2">        );</span></span>
<span class="line"><span style="color:#F8F8F2">      }</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // Retrieve value after refresh</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> previousValue </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> sessionStorage.</span><span style="color:#50FA7B">getItem</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#E9F284">          '</span><span style="color:#F1FA8C">clever-textarea-value</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">    );</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (textarea) {</span></span>
<span class="line"><span style="color:#F8F8F2">      textarea.value </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> previousValue;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FF79C6">textarea</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">textarea</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>If you refresh your browser you should now find a pre-filled <code>textarea</code> which should contains the last value you would have entered before refresh.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*6mED0Asr6LRfVGqEsNPIjg.png" alt="" / loading="lazy"><br />
<span class="figcaption_hack">textarea should be pre-filled with your previous value after refresh</span></p>
<h4>Add the support for multiple inputs in the same page</h4>
<p>Well, that’s neat, we were able to save and load the draft of your input, but what would happen if we would use multiple times the same component in a page as we use a unique name to save the entry in the storage? Yes, right, it would be weird and contains a unique value…</p>
<p>To overcome this problem we are going to improve our code to add and use a variable value for our session storage key. For that purpose we are going to add a Stencil <a href="https://stenciljs.com/docs/properties" rel="noopener noreferrer external">properties</a> which expose a public attribute to the component.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Component, Element, Prop} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@stencil/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  tag</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">my-component</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  styleUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">my-component.css</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  shadow</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> MyComponent</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  @</span><span style="color:#50FA7B">Element</span><span style="color:#F8F8F2">() el</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLElement</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">  @</span><span style="color:#50FA7B">Prop</span><span style="color:#F8F8F2">() key</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  componentDidLoad</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> textarea </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.el.shadowRoot.</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">textarea</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // Save input value before refresh</span></span>
<span class="line"><span style="color:#F8F8F2">    window.</span><span style="color:#50FA7B">addEventListener</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">beforeunload</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,  (</span><span style="color:#FFB86C;font-style:italic">_event</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">      if</span><span style="color:#F8F8F2"> (textarea </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.key) {</span></span>
<span class="line"><span style="color:#F8F8F2">        sessionStorage.</span><span style="color:#50FA7B">setItem</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">          this</span><span style="color:#F8F8F2">.key, textarea.value</span></span>
<span class="line"><span style="color:#F8F8F2">        );</span></span>
<span class="line"><span style="color:#F8F8F2">      }</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">    // Retrieve value after refresh</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> previousValue </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> sessionStorage.</span><span style="color:#50FA7B">getItem</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.key);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (textarea) {</span></span>
<span class="line"><span style="color:#F8F8F2">      textarea.value </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> previousValue;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  render</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> &#x3C;</span><span style="color:#FF79C6">textarea</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">textarea</span><span style="color:#F8F8F2">>;</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Once the code modified, we could now modify the HTML page we are using for test purpose in order to specify this attribute and even add another component to the page. For that purpose, you could modify <code>src/index.html</code> like the following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">body</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;</span><span style="color:#8BE9FD;font-style:italic">my-component</span><span style="color:#50FA7B;font-style:italic"> key</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">clever-textarea-value-1</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">my-component</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  &#x3C;</span><span style="color:#8BE9FD;font-style:italic">my-component</span><span style="color:#50FA7B;font-style:italic"> key</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">clever-textarea-value-2</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">my-component</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">body</span><span style="color:#F8F8F2">></span></span></code></pre><p><em>As for the JSX code, you could safely remove the previous demo attributes “last” and “first” which comes with the Stencil starter component as we don’t use them in this tutorial.</em></p>
<p>If we go back to your browser you should now find two <code>textarea</code> respectively two components. You could now try to fill them and again try to refresh your browser.</p>
<p><img src="https://daviddalbusco.com/assets/images/1*XQB1PxxL6tTWOh7xy7tJ8g.png" alt="" / loading="lazy"><br />
<span class="figcaption_hack">fill the two components before refresh</span></p>
<p><img src="https://daviddalbusco.com/assets/images/1*iAebxf4nzQI37wCyin02fw.png" alt="" / loading="lazy"><br />
<span class="figcaption_hack">after refresh of the browser</span></p>
<p>Hooray we were able to use two clever <code>textarea</code> Web Components in our page 🎉</p>
<h3>In conclusion</h3>
<p>Of course the above code would still need a bit of improvements, I would notably separate the code in methods, add some promises, because there are never enough promises 😋, and maybe even clear the storage after having read the value but I hope that this article would have give you some ideas about how to implement such a “clever” <code>input</code> or <code>textarea</code> and furthermore to that, if you never tried <a href="https://stenciljs.com/" rel="noopener noreferrer external">Stencil</a> before, made you a bit curious about it because again, this compiler is <strong>amazing</strong> 🤘</p>
<h3>Cherry on the cake 🍒🎂</h3>
<p>Web Components could be integrated in <strong>any</strong> modern frameworks (the Stencil documentation provide examples of framework integration for <a href="https://stenciljs.com/docs/angular" rel="noopener noreferrer external">Angular</a>, <a href="https://stenciljs.com/docs/react" rel="noopener noreferrer external">React</a>, <a href="https://stenciljs.com/docs/vue" rel="noopener noreferrer external">Vue</a> and <a href="https://stenciljs.com/docs/ember" rel="noopener noreferrer external">Ember</a>) <strong>or even without</strong> any framework (like I do in the DeckDeckGo, give it a try for your next presentation 👉 <code>npm init deckdeckgo</code>).</p>
<p>To infinity and beyond 🚀</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Using Ionic without any framework]]></title>
          <description><![CDATA[Using Ionic without any framework]]></description>
          <link>https://daviddalbusco.com/blog/using-ionic-without-any-framework/</link>
          <pubDate>Fri, 19 Oct 2018 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*qMT7WHMPzBZai4ZikSlLTg.jpeg" alt="" / loading="lazy"></p>
<p>While I was preparing my last talk about Web Components and <a href="https://ionicframework.com/" rel="noopener noreferrer external">Ionic</a> v4, I noticed that I was using none of these to actually build my slides 🤔 That’s why I published this week <a href="https://deckdeckgo.com/" rel="noopener noreferrer external">DeckDeckGo</a>, the new tool to create lightweight presentation using HTML and Ionic components 🚀</p>
<p>To follow-up what I learned while wrapping up together this new pet project, I thought it would be maybe interesting to wrote a brief article about how to use Ionic without any framework or with any framework, as you rather like😉</p>
<h3>Importing Ionic</h3>
<p>The goal of this article is to describe how to use Ionic in a simple <code>index.html</code> therefore we won’t rely on <a href="https://npmjs.com/" rel="noopener noreferrer external">NPM</a> and we are going to import Ionic from a CDN, most particularly from <a href="https://unpkg.com/#/" rel="noopener noreferrer external">Unpkg</a>. To do so we are just then going to add the following lines to the <code>&lt;head/&gt;</code> of your HTML page:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">&#x3C;!--</span><span style="color:#F8F8F2"> Import the Ionic CSS </span><span style="color:#FF79C6">--></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">link</span><span style="color:#50FA7B;font-style:italic"> href</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://unpkg.com/@ionic/core@latest/css/ionic.bundle.css</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> rel</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">stylesheet</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;!-- Import Ionic --></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#50FA7B;font-style:italic"> src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://unpkg.com/@ionic/core@latest/dist/ionic.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;!-- Optional, import the Ionic icons --></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#50FA7B;font-style:italic"> src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://unpkg.com/ionicons@latest/dist/ionicons.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span></code></pre><p>And that’s it, that’s all you need to import Ionic in any web applications 🎉</p>
<h3>A basic usage</h3>
<p>Well, I could stop the article here, it’s super easy and straight forward, but I thought it would be good to display some example of usages too. In this first example, I just gonna showcase a button which should trigger an alert.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">&#x3C;!</span><span style="color:#F8F8F2">doctype html</span><span style="color:#FF79C6">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">html</span><span style="color:#50FA7B;font-style:italic"> lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">en</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">head</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">meta</span><span style="color:#50FA7B;font-style:italic"> charset</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">utf-8</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">title</span><span style="color:#F8F8F2">>Using Ionic without any framework&#x3C;/</span><span style="color:#FF79C6">title</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;!-- Import the Ionic CSS --></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">link</span><span style="color:#50FA7B;font-style:italic"> href</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://unpkg.com/@ionic/core@latest/css/ionic.bundle.css</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> rel</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">stylesheet</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;!-- Import Ionic --></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#50FA7B;font-style:italic"> src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://unpkg.com/@ionic/core@latest/dist/ionic.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;!-- Optional, import the Ionic icons --></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#50FA7B;font-style:italic"> src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://unpkg.com/ionicons@latest/dist/ionicons.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">head</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">body</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;!-- We create a vanilla Javascript function to call the alert --></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    hello = async function () </span><span style="color:#FF79C6">{</span></span>
<span class="line"><span style="color:#50FA7B">        alert</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Hello World!</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">    }</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;!-- We declare an Ionic app using the &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-app</span><span style="color:#F8F8F2">/> element --></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-app</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;!-- Cool thing, the Ionic CSS utilities could be used too --></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-content</span><span style="color:#50FA7B;font-style:italic"> text-center</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">>Basic usage&#x3C;/</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;!-- We add an ion-button with an onclick event --></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-button</span><span style="color:#50FA7B;font-style:italic"> onclick</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">hello()</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Click me&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ion-button</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ion-content</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ion-app</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">body</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">html</span><span style="color:#F8F8F2">></span></span></code></pre><h4>Codepen example</h4>
<p>Because we use Ionic in an <code>index.html</code> without any framework, it gives the ability to create really easily <a href="https://codepen.io/" rel="noopener noreferrer external">Codepen</a> examples. You could check this particular one at the following address: <a href="https://codepen.io/peterpeterparker/pen/YJLvQq" rel="noopener noreferrer external">https://codepen.io/peterpeterparker/pen/YJLvQq</a></p>
<h3>A bit more tricky usage</h3>
<p>The previous example is really straight forward, but what about more “complex” components, like creating and using modals or a popovers?</p>
<h4>Modals</h4>
<p>To be honest, describing how to implement a modal would just be a rip off of the documentation. Therefore, if you would like to implement an Ionic modal in a project without or with an frameworks, just visit the <a href="https://beta.ionicframework.com/docs/api/modal" rel="noopener noreferrer external">modal documentation</a>, go to chapter “Usage” , toggle to “Javascript” the example, copy/paste the code in your project and voilà, it just gonna works out of the box 😜</p>
<h4>Popovers</h4>
<p>I found the implementation of a popover a bit more tricky (to some extension, it’s still super easy 😆). We will try now to move the button of the previous example, which trigger the alert, from the main page to a popover.</p>
<p>First thing first, to implement such a component, we have to add a new<code>&lt;ion-popover-controller/&gt;</code> element to our HTML page. I personally understand this as an anchor for the element we are about to add, which will, btw. only be added to the DOM once called and not before.</p>
<p>The second step is to to create a custom element which will be in charge to parse the content of our popover.</p>
<p>Finally, as we would do in a classic Ionic <a href="https://angular.io/" rel="noopener noreferrer external">Angular</a> application, we will use the <code>PopoverController</code> to create and present it.</p>
<p>Wrapped up together, the <code>index.html</code> page would now looks like the following:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">&#x3C;!</span><span style="color:#F8F8F2">doctype html</span><span style="color:#FF79C6">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">html</span><span style="color:#50FA7B;font-style:italic"> lang</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">en</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">head</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">meta</span><span style="color:#50FA7B;font-style:italic"> charset</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">utf-8</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">title</span><span style="color:#F8F8F2">>Using Ionic without any framework&#x3C;/</span><span style="color:#FF79C6">title</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">link</span><span style="color:#50FA7B;font-style:italic"> href</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://unpkg.com/@ionic/core@latest/css/ionic.bundle.css</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> rel</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">stylesheet</span><span style="color:#E9F284">"</span><span style="color:#50FA7B;font-style:italic"> media</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">screen</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#50FA7B;font-style:italic"> src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://unpkg.com/@ionic/core@latest/dist/ionic.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#50FA7B;font-style:italic"> src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://unpkg.com/ionicons@latest/dist/ionicons.js</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">head</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">body</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    hello = async function () </span><span style="color:#FF79C6">{</span></span>
<span class="line"><span style="color:#50FA7B">        alert</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Hello World!</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">    }</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    // Our custom element</span></span>
<span class="line"><span style="color:#F8F8F2">    class MyPopover extends HTMLElement </span><span style="color:#FF79C6">{</span></span>
<span class="line"><span style="color:#50FA7B">        constructor</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#50FA7B">            super</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">        async </span><span style="color:#50FA7B">connectedCallback</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">            // Here we parse the content of the popover</span></span>
<span class="line"><span style="color:#F8F8F2">            this.innerHTML </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">&#x3C;ion-content>&#x3C;ion-button onclick="hello()">Click me&#x3C;/ion-button>&#x3C;/ion-content></span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#FF79C6">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    // We define our custom element</span></span>
<span class="line"><span style="color:#F8F8F2">    customElements.define('my-popover', MyPopover);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    // The function to bring the pieces together</span></span>
<span class="line"><span style="color:#F8F8F2">    presentMyPopover = async function() </span><span style="color:#FF79C6">{</span></span>
<span class="line"><span style="color:#6272A4">        // We get the anchor</span></span>
<span class="line"><span style="color:#F8F8F2">        const popoverController </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> document.</span><span style="color:#50FA7B">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">ion-popover-controller</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        await</span><span style="color:#F8F8F2"> popoverController.</span><span style="color:#50FA7B">componentOnReady</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">        // We create the popover for our custom element</span></span>
<span class="line"><span style="color:#F8F8F2">        const popover </span><span style="color:#FF79C6">=</span><span style="color:#FF79C6"> await</span><span style="color:#F8F8F2"> popoverController.</span><span style="color:#50FA7B">create</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">            component</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">my-popover</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">            translucent</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4">        // We present the popover</span></span>
<span class="line"><span style="color:#F8F8F2">        return </span><span style="color:#FF79C6">await</span><span style="color:#F8F8F2"> popover.</span><span style="color:#50FA7B">present</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">    }</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-app</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-content</span><span style="color:#50FA7B;font-style:italic"> text-center</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">>Popover usage&#x3C;/</span><span style="color:#FF79C6">h1</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;!-- On click we call the function to open the popover --></span></span>
<span class="line"><span style="color:#F8F8F2">        &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-button</span><span style="color:#50FA7B;font-style:italic"> onclick</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">presentMyPopover()</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>Open&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ion-button</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ion-content</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;!-- Our popover anchor --></span></span>
<span class="line"><span style="color:#F8F8F2">    &#x3C;</span><span style="color:#8BE9FD;font-style:italic">ion-popover-controller</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ion-popover-controller</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#8BE9FD;font-style:italic">ion-app</span><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">body</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">&#x3C;/</span><span style="color:#FF79C6">html</span><span style="color:#F8F8F2">></span></span></code></pre><h4>Codepen</h4>
<p>You could check this second example at the following address: <a href="https://codepen.io/peterpeterparker/pen/JmvZLo" rel="noopener noreferrer external">https://codepen.io/peterpeterparker/pen/JmvZLo</a></p>
<h3>Custom elements and browser compatibility</h3>
<p>Custom elements are now natively supported by Chrome and Safari but are not yet supported per default in Firefox &lt; v63. To overcome this problem we could add a the <a href="https://github.com/webcomponents/custom-elements" rel="noopener noreferrer external">custom elements Polyfill</a> in the header of our HTML page:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">script</span><span style="color:#50FA7B;font-style:italic"> src</span><span style="color:#FF79C6">=</span><span style="color:#E9F284">"</span><span style="color:#F1FA8C">https://unpkg.com/@webcomponents/custom-elements</span><span style="color:#E9F284">"</span><span style="color:#F8F8F2">>&#x3C;/</span><span style="color:#FF79C6">script</span><span style="color:#F8F8F2">></span></span></code></pre><h3>Cherry on the cake 🍒🎂</h3>
<p>As I said in my introduction, I have published my new pet project <a href="https://deckdeckgo.com/" rel="noopener noreferrer external">DeckDeckGo</a> , where I used these components, this week. But, what I didn’t mentioned so far is, that this project is free and open source 🚀 Therefore you could find a practical implementation of everything I tried to display above in my DeckDeckGo repo: <a href="https://github.com/fluster/deckdeckgo" rel="noopener noreferrer external">https://github.com/fluster/deckdeckgo</a></p>
<p>To infinity and beyond 🚀</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[How to declare and use modals in Ionic v4]]></title>
          <description><![CDATA[How to declare and use modals in Ionic v4]]></description>
          <link>https://daviddalbusco.com/blog/how-to-declare-and-use-modals-in-ionic-v4/</link>
          <pubDate>Tue, 09 Oct 2018 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*jJJ6qFutpkXsD-aRYGs1tA.jpeg" alt="" / loading="lazy"></p>
<p>I noticed that questions about the declaration and use of modals in <a href="https://ionicframework.com/" rel="noopener noreferrer external">Ionic</a> v4 with <a href="https://angular.io/" rel="noopener noreferrer external">Angular</a> often pops up, therefore I thought I would briefly describe how I solved this subject in my mobile application <a href="https://fluster.io/" rel="noopener noreferrer external">Fluster</a>.</p>
<h3>Creating a modal</h3>
<p>A modal is a <code>component</code> and its annotation doesn’t really change in comparison to Ionic v3.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Component} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    templateUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">date-picker.html</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    styleUrls</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">./date-picker.scss</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">    selector</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-date-picker</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> DatePickerModal</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><h4>Declaring the modal’s module</h4>
<p>What do change is probably the declaration of the modal’s module. In Ionic v3, the modal was bootstraped with the help of the <code>@IonicPage()</code> annotation, which doesn’t exist anymore in Ionic v4. Therefore we have tells our module that our modal has to be loaded imperatively, this could be achieved by declaring it as an **entry component **in its module.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {NgModule} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {IonicModule} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@ionic/angular</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {CommonModule} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/common</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#6272A4">// The modal's component of the previous chapter</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {DatePickerModal} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./date-picker</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">NgModule</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">     declarations</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">       DatePickerModal</span></span>
<span class="line"><span style="color:#F8F8F2">     ],</span></span>
<span class="line"><span style="color:#F8F8F2">     imports</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">       IonicModule</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">       CommonModule</span></span>
<span class="line"><span style="color:#F8F8F2">     ],</span></span>
<span class="line"><span style="color:#F8F8F2">     entryComponents</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">       DatePickerModal</span></span>
<span class="line"><span style="color:#F8F8F2">     ]</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> DatePickerModalModule</span><span style="color:#F8F8F2"> {}</span></span></code></pre><h4>Lazy loading</h4>
<p>You might ask yourself, are these modals lazy loaded? The answer to this is one of my favorite german word: <strong>jein</strong>, which is a contraction of <em>ja</em> (= yes) and <em>nein</em> (= no).</p>
<p>The modal is lazy loaded, BUT, it will not be loaded when you open it, as you probably would except, but rather when the module, where the modal’s module is use, will be loaded.</p>
<p>For more information about this particular subject you could visit and participate to the following Ionic forum topic (btw. there I’m <em>reedrichards</em>):<br />
<a href="https://forum.ionicframework.com/t/ionic-4-lazy-loading-and-modals" rel="noopener noreferrer external">https://forum.ionicframework.com/t/ionic-4-lazy-loading-and-modals</a></p>
<h3>Using the modal</h3>
<p>First thing we have to do in order to use our modal is importing it in the module of the page or component where we will use it.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {NgModule} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {CommonModule} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/common</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {IonicModule} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@ionic/angular</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#6272A4">// The modal's module of the previous chapter</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {DatePickerModalModule} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">../../../../modals/core/date-picker/date-picker.module</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">NgModule</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    declarations</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">        NewAdStepAvailabilityComponent</span></span>
<span class="line"><span style="color:#F8F8F2">    ],</span></span>
<span class="line"><span style="color:#F8F8F2">    imports</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">      IonicModule</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">      CommonModule</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">      DatePickerModalModule</span></span>
<span class="line"><span style="color:#F8F8F2">    ],</span></span>
<span class="line"><span style="color:#F8F8F2">    exports</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">      NewAdStepAvailabilityComponent</span></span>
<span class="line"><span style="color:#F8F8F2">    ]</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> NewAdStepAvailabilityModule</span><span style="color:#F8F8F2"> {}</span></span></code></pre><p><em>Note: About lazy loading and this particular example, the modal is loaded in the same time as the module <em><code>NewAdStepAvailabilityModule</code></em> respectively, in our browser, we would be able to observe that the javascript code of the modal is loaded in the same time as the javascript code of this particular module.</em></p>
<h4>Calling the modal and passing parameters</h4>
<p>Another question which often pops up is “how to pass parameters and get a result back when using modals in Ionic v4”.</p>
<p>As in Ionic v3, we use the <code>ModalController</code> to create, and interact with, a modal. This controller offers a <code>create</code> method which will allow us to pass parameters (specified in the following example with <code>componentProps)</code> and a <code>onDidDismiss</code> event which will allow us to listen to the modal‘s closing action in order to get the result.</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">async </span><span style="color:#50FA7B">openModal</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> modal</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HTMLIonModalElement</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#FF79C6">       await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.modalController.</span><span style="color:#50FA7B">create</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">          component</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> DatePickerModal,</span></span>
<span class="line"><span style="color:#F8F8F2">          componentProps</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">             aParameter</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">             otherParameter</span><span style="color:#FF79C6">:</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Date</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">          }</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    modal.</span><span style="color:#50FA7B">onDidDismiss</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">then</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">detail</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> OverlayEventDetail</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">       if</span><span style="color:#F8F8F2"> (detail </span><span style="color:#FF79C6">!==</span><span style="color:#BD93F9"> null</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">         console.</span><span style="color:#50FA7B">log</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">The result:</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, detail.data);</span></span>
<span class="line"><span style="color:#F8F8F2">       }</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#F8F8F2"> modal.</span><span style="color:#50FA7B">present</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><h4>Getting the parameters in the modal and closing the modal with a result</h4>
<p>In our modal, the <code>NavParams</code> provider could be use to read the parameters we specified above when we created the modal.</p>
<p>Finally, closing the modal and passing a result back could be accomplished using the <code>ModalController</code> .</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Component} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {ModalController, NavParams} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@ionic/angular</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    templateUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">date-picker.html</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    styleUrls</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">./date-picker.scss</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">    selector</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-date-picker</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> DatePickerModal</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">    myParameter</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> boolean</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    myOtherParameter</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Date</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">    constructor</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">private</span><span style="color:#FFB86C;font-style:italic"> modalController</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> ModalController</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#FF79C6">                private</span><span style="color:#FFB86C;font-style:italic"> navParams</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> NavParams</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#50FA7B">    ionViewWillEnter</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">      this</span><span style="color:#F8F8F2">.myParameter </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.navParams.</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">aParameter</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">      this</span><span style="color:#F8F8F2">.myOtherParameter </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.navParams.</span><span style="color:#50FA7B">get</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">otherParameter</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#FF79C6">    async</span><span style="color:#50FA7B"> myDismiss</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">      const</span><span style="color:#F8F8F2"> result</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Date</span><span style="color:#FF79C6"> =</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#50FA7B"> Date</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">      await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.modalController.</span><span style="color:#50FA7B">dismiss</span><span style="color:#F8F8F2">(result);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p><em>Note: In this example I used boolean and dates for the parameters and result, but these could also be any other types or even a custom object.</em></p>
<h3>Cherry on the cake 🍒🎂</h3>
<p>It works exactly the same with popovers 😉</p>
<p>To infinity and beyond 🚀</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Chain a social login from your website to your PWA]]></title>
          <description><![CDATA[Chain a social login from your website to your PWA]]></description>
          <link>https://daviddalbusco.com/blog/chain-a-social-login-from-your-website-to-your-pwa/</link>
          <pubDate>Thu, 20 Sep 2018 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*IXO653m7EDs58poOsnn5cg.gif" alt="" / loading="lazy"></p>
<p>Great news, after having open-sourced my mobile and progressive web app <a href="https://fluster.io/" rel="noopener noreferrer external">Fluster</a> last week, I follow up my move by releasing today my website on <a href="https://github.com/fluster/fluster-website" rel="noopener noreferrer external">GitHub</a> 🎉</p>
<p>Beside maybe the fact that this website is build with Angular, run as a server-side rendered app (SSR) and contains a simple blog based on <a href="https://dev.to/daviddalbusco/add-a-blog-to-your-angular-website-using-markdown-files-go7" rel="noopener noreferrer external">markdown files</a>, it looks probably pretty common. But, if you give it a second chance, you might notice, I hope, something interesting: this website offers its users the ability to perform a login with their social accounts, Google or Facebook, and to jump afterwards directly to my progressive web app 😋</p>
<h3>Social login flows with a PWA</h3>
<p>You may ask yourself how to implement social login in PWA? I won’t cover that particular subject in this article, but if needed, to point you in the right direction, I have implemented an “<a href="https://developers.google.com/identity/protocols/OpenIDConnect" rel="noopener noreferrer external">OpenID connect</a>” flow for the Google login and a “<a href="https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/" rel="noopener noreferrer external">manual login flow</a>” for the Facebook one.</p>
<p>The idea beside these two flows, is to redirect the users, after they successfully logged themselves in their social accounts, to our application with temporary tokens in order to to validate and to complete the process with our backend.</p>
<h3>Chaining a social login from a website to a PWA</h3>
<p>The challenge of such a login flow is that it has to happens between two different applications (the website and the PWA). Furthermore, to add just a bit of complexity, both applications might be served in two different subdomains. I’ll try to explain and display how it could be solve easily with the example of a Google login flow.</p>
<h4>Website</h4>
<p>First, the easy part, we add a button to our website:</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">button</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">(click)="googleLogin()">Log</span><span style="color:#50FA7B;font-style:italic"> me</span><span style="color:#50FA7B;font-style:italic"> in</span><span style="color:#50FA7B;font-style:italic"> with</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">Google&#x3C;/button></span></span></code></pre><p>Then we implement the function <code>googleLogin()</code> which does the following:</p>
<ul>
<li>It generates a random <code>state</code> to identify the login flow</li>
<li>Writes this <code>state</code> in a cookie (which should be available across our<br />
subdomains)</li>
<li>It finally redirects the user to her/his social account</li>
</ul>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">constructor</span><span style="color:#F8F8F2">(@</span><span style="color:#50FA7B">Inject</span><span style="color:#F8F8F2">(DOCUMENT) private document: Document) {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">googleLogin</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> state </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">generateRandomString</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">16</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> url </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">https://accounts.google.com/o/oauth2/v2/auth?</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> webClientId </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">my-google-web-client-id</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#6272A4">  // The redirect url to be called after a successful login</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> pwaUrl </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">https://m.mydomain.com/</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#6272A4">  // First we save the state in a cookie</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">  this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">writeCookieState</span><span style="color:#F8F8F2">(state, </span><span style="color:#BD93F9">true</span><span style="color:#F8F8F2">).</span><span style="color:#50FA7B">then</span><span style="color:#F8F8F2">(() </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#6272A4">     // Then we build our social login url</span></span>
<span class="line"><span style="color:#FF79C6">     const</span><span style="color:#F8F8F2"> googleUrl</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> url </span><span style="color:#FF79C6">+</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">client_id=</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#FF79C6">     +</span><span style="color:#F8F8F2"> webClientId </span><span style="color:#FF79C6">+</span></span>
<span class="line"><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">&#x26;response_type=code&#x26;scope=openid%20profile%20email&#x26;redirect_uri=</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#FF79C6">         +</span><span style="color:#50FA7B"> encodeURIComponent</span><span style="color:#F8F8F2">(pwaUrl)</span></span>
<span class="line"><span style="color:#FF79C6">         +</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">&#x26;nonce=</span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> +</span><span style="color:#F8F8F2"> state </span><span style="color:#FF79C6">+</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">&#x26;state=</span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> +</span><span style="color:#F8F8F2"> state;</span></span>
<span class="line"><span style="color:#6272A4">     // And we redirect the user to his google account</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">     this</span><span style="color:#F8F8F2">.document.location.href </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> googleUrl;</span></span>
<span class="line"><span style="color:#F8F8F2">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">private </span><span style="color:#50FA7B">writeCookieState</span><span style="color:#F8F8F2">(state: string, googleAuth: boolean)</span></span>
<span class="line"><span style="color:#F8F8F2">        : </span><span style="color:#8BE9FD;font-style:italic">Promise</span><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2">{}</span><span style="color:#FF79C6">></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  return new </span><span style="color:#50FA7B">Promise</span><span style="color:#F8F8F2">((</span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6272A4"> // Note here the specific domain beginning with a point</span></span>
<span class="line"><span style="color:#6272A4">// That’s one of the key in order to be able to read the cookie from the subdomain</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">    const</span><span style="color:#F8F8F2"> baseDomain </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">.mydomain.com’</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">;</span></span>
<span class="line"><span style="color:#8BE9FD;font-style:italic">    const</span><span style="color:#F8F8F2"> expireAfter</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Date</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#50FA7B">          moment</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6;font-weight:bold">new</span><span style="color:#50FA7B"> Date</span><span style="color:#F8F8F2">()).</span><span style="color:#50FA7B">add</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">5</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">m’).toDate()</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.document.cookie </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">Our_state={"state":"</span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> +</span><span style="color:#F8F8F2"> state </span><span style="color:#FF79C6">+</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">", "googleAuth": </span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> +</span><span style="color:#F8F8F2"> googleAuth </span><span style="color:#FF79C6">+</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">}; expires=</span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> +</span><span style="color:#F8F8F2"> expireAfter </span><span style="color:#FF79C6">+</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">; domain=</span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> +</span><span style="color:#F8F8F2"> baseDomain </span><span style="color:#FF79C6">+</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">; path=/’</span><span style="color:#FF5555;font-style:italic;text-decoration:underline">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    resolve</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">  });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"><span style="color:#50FA7B">generateRandomString</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">length</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> number</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">  let</span><span style="color:#F8F8F2"> text </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> ''</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">  const</span><span style="color:#F8F8F2"> possible </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  for</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">let</span><span style="color:#F8F8F2"> i </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">; i </span><span style="color:#FF79C6">&#x3C;</span><span style="color:#F8F8F2"> length; i</span><span style="color:#FF79C6">++</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">    text </span><span style="color:#FF79C6">+=</span><span style="color:#F8F8F2"> possible.</span><span style="color:#50FA7B">charAt</span><span style="color:#F8F8F2">(Math.</span><span style="color:#50FA7B">floor</span><span style="color:#F8F8F2">(Math.</span><span style="color:#50FA7B">random</span><span style="color:#F8F8F2">() </span><span style="color:#FF79C6">*</span><span style="color:#F8F8F2"> possible.length));</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  return</span><span style="color:#F8F8F2"> text;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Good, nothing much to do in our website. We reached the point where we saved the <code>state</code> in a cookie and we redirected the user to her/his social account. Once successfully logged in, she/he will be redirected again but this time to our other application because we provided as parameter a redirection URL, not to our website, but to our PWA.</p>
<h4>Progressive Web App</h4>
<p>I will not go to deep in details about how to validate and complete the login flow in the PWA, but here are the key points regarding the particular subject of this article which I did implement in my app build with <a href="https://ionicframework.com/" rel="noopener noreferrer external">Ionic</a>:</p>
<ol>
<li>The redirection to our PWA will happens with an url containing a <code>code</code> and a <code>state</code> as parameters. To catch these information, we could intercept them in <code>app.component.ts</code> and save them in a provider:</li>
</ol>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">ngOnInit</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> state</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.platform.</span><span style="color:#50FA7B">getQueryParam</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">state</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> code</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.platform.</span><span style="color:#50FA7B">getQueryParam</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">code</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.loginService.</span><span style="color:#50FA7B">setState</span><span style="color:#F8F8F2">(state);</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.loginService.</span><span style="color:#50FA7B">setCode</span><span style="color:#F8F8F2">(code);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><ol start="2">
<li>Once our PWA is fully loaded, we could start automatically the login process.<br />
Before displaying some codes, here a summary of what we are going to do:</li>
</ol>
<ul>
<li>First we will check if a <code>state</code> and a <code>code</code> have been read and added to our<br />
provider</li>
<li>Then we will try to retrieve the <code>state</code> we previously saved in the cross-domain<br />
cookie</li>
<li>Finally we will compare both states to validate the flow, respectively to<br />
validate the fact that the user who tries to log in is effectively the user who<br />
wants to log in</li>
</ul>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#50FA7B">constructor</span><span style="color:#F8F8F2">(@</span><span style="color:#50FA7B">Inject</span><span style="color:#F8F8F2">(DOCUMENT) private document: Document) {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"><span style="color:#F8F8F2">private </span><span style="color:#50FA7B">ionViewWillEnter</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> state</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.loginService.</span><span style="color:#50FA7B">getState</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> code</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.loginService.</span><span style="color:#50FA7B">getCode</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (state </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> code) {</span></span>
<span class="line"><span style="color:#FF79C6">        const</span><span style="color:#F8F8F2"> cookieState </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">getPwaLoginStateCookie</span><span style="color:#F8F8F2">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (cookieState </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> state </span><span style="color:#FF79C6">===</span><span style="color:#F8F8F2"> cookieState.state) {</span></span>
<span class="line"><span style="color:#6272A4">           // All good, we could do the login</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">           this</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">doPwaLoginAndNavigate</span><span style="color:#F8F8F2">(cookiePwaLoginState, code);</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"><span style="color:#F8F8F2">private </span><span style="color:#50FA7B">getPwaLoginStateCookie</span><span style="color:#F8F8F2">(): any {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.document.cookie) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#BD93F9"> null</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> cookies</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> RegExpExecArray</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#50FA7B">       RegExp</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">Our_state</span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> +</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">[^;]+</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">).</span><span style="color:#50FA7B">exec</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.document.cookie);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">cookies) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#BD93F9"> null</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#FF79C6">    const</span><span style="color:#F8F8F2"> cookie</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#FF79C6"> =</span><span style="color:#50FA7B"> decodeURIComponent</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">!!</span><span style="color:#F8F8F2">cookies </span><span style="color:#FF79C6">?</span><span style="color:#F8F8F2"> cookies.</span><span style="color:#50FA7B">toString</span><span style="color:#F8F8F2">().</span><span style="color:#50FA7B">replace</span><span style="color:#F8F8F2">(</span><span style="color:#FF5555">/</span><span style="color:#FF79C6">^</span><span style="color:#8BE9FD">[</span><span style="color:#FF5555">^</span><span style="color:#F1FA8C">=</span><span style="color:#8BE9FD">]</span><span style="color:#FF79C6">+</span><span style="color:#BD93F9">.</span><span style="color:#FF5555">/</span><span style="color:#F8F8F2">, </span><span style="color:#E9F284">''</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> ''</span><span style="color:#F8F8F2">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#FF79C6">!</span><span style="color:#F8F8F2">cookie </span><span style="color:#FF79C6">||</span><span style="color:#F8F8F2"> cookie.</span><span style="color:#50FA7B">indexOf</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">state</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">===</span><span style="color:#FF79C6"> -</span><span style="color:#BD93F9">1</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#BD93F9"> null</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#BD93F9"> JSON</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">parse</span><span style="color:#F8F8F2">(cookie);</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Voilà, we were able to chain a login flow from our website to our PWA 😇</p>
<h3>Cherry on the cake 🍒🎂</h3>
<p>As I said in my introduction, <a href="https://fluster.io/" rel="noopener noreferrer external">Fluster</a> is open source, therefore, if the above code samples are a bit hard to swallow, just clone my <a href="https://github.com/fluster/fluster-website" rel="noopener noreferrer external">website</a> and <a href="https://github.com/fluster/fluster-app" rel="noopener noreferrer external">app</a>. It will be, I hope, easier to follow once you have opened them in your favorite editor 😉</p>
<p>To infinity and beyond 🚀</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Add a blog to your Angular website using markdown files]]></title>
          <description><![CDATA[Add a blog to your Angular website using markdown files]]></description>
          <link>https://daviddalbusco.com/blog/add-a-blog-to-your-angular-website-using-markdown-files/</link>
          <pubDate>Fri, 07 Sep 2018 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*MTFh-iQIpiz6mfzB93SWkA.jpeg" alt="" / loading="lazy"></p>
<p>Last week I wanted to add a blog to my <a href="https://angular.io/" rel="noopener noreferrer external">Angular</a> Universal website, but I didn’t wanted to implement a complex solution and spend to much time on it. Neither did I wanted to add a CMS or even store the articles in a database. That’s why I came up, I think, with a pretty handy, for not saying dumb simple, solution with the implementation of a blog based on <a href="https://en.wikipedia.org/wiki/Markdown" rel="noopener noreferrer external">markdown</a> files 🚀</p>
<p><em>Before going further: If you are looking to implement a blog, you are looking to share your stories but you are also most probably looking to make your website more SEO friendly. Therefore, I assume that you already have implemented an <a href="https://github.com/angular/universal-starter" rel="noopener noreferrer external">Angular SSR</a> website. If not, I would really advise you to have a look to that particular topic and if you do have, don’t miss the very last chapter of this article, a kind of hack is needed in order to load the resources correctly on the backend side</em></p>
<h3>Installation</h3>
<p>The only required extra library we will need to implement the solution is <a href="https://github.com/jfcere/ngx-markdown" rel="noopener noreferrer external">ngx-markdown</a> of [Jean-Francois Cere (https://github.com/jfcere). It will add the markdown support to our website respectively this awesome library will do all the job for us, it will read the markdown files and parse them to html 💪😃</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">npm install ngx</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">markdown </span><span style="color:#FF79C6">--</span><span style="color:#F8F8F2">save</span></span></code></pre><h3>Content</h3>
<p>As I said above, the idea is to use markdown files as sources for the blog. In this solution I ship the files within the app, placing them under the <code>assets</code> folder</p>
<ul>
<li>assets &gt; blog &gt; blog.md: the list of blog entries</li>
<li>assets &gt; blog &gt; post &gt; *.md: the articles (= blog posts) themselves</li>
</ul>
<h3>Routes</h3>
<p>For this solution we will need to add two routes to our website</p>
<ul>
<li>A route “/blog” which will display a list of all articles</li>
<li>A route “/blog/post/name-of-the-article” which will display a particular blog post. In this route example the blog post name is “name-of-the-article”</li>
</ul>
<p>I did group all routes under the same “blog” path, therefore the structure will look like the following once implemented</p>
<p><img src="https://daviddalbusco.com/assets/images/1*K_WltAXjubVdyXWMUN7Tmg.png" alt="" / loading="lazy"></p>
<h3>Implementation</h3>
<p>First of all, we create a main <code>blog.module.ts</code> which will route the submodules and import the markdown library</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">NgModule</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  imports</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    CommonModule</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#50FA7B">    RouterModule</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">forChild</span><span style="color:#F8F8F2">([</span></span>
<span class="line"><span style="color:#F8F8F2">      {path</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> ''</span><span style="color:#F8F8F2">, pathMatch</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">full</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">               loadChildren</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./blog/blog-view.module#BlogViewModule</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">},</span></span>
<span class="line"><span style="color:#F8F8F2">      {path</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">post</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">               loadChildren</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./post/blog-post-view.module#BlogPostViewModule</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">},</span></span>
<span class="line"><span style="color:#F8F8F2">    ]),</span></span>
<span class="line"><span style="color:#50FA7B">    MarkdownModule</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">forRoot</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">  ]</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span></code></pre><h4>Blog</h4>
<p>Now we could create the <code>blog-view</code> which will display the list of blog entries with the help of the Angular cli. To the default moduleswe only have to add the import of the markdown module</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">NgModule</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  declarations</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#50FA7B;font-style:italic">BlogViewComponent</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">  imports</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    CommonModule</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#50FA7B">    RouterModule</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">forChild</span><span style="color:#F8F8F2">([</span></span>
<span class="line"><span style="color:#F8F8F2">      { path</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> ''</span><span style="color:#F8F8F2">, component</span><span style="color:#FF79C6">:</span><span style="color:#50FA7B;font-style:italic"> BlogViewComponent</span><span style="color:#F8F8F2">}</span></span>
<span class="line"><span style="color:#F8F8F2">    ]),</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    ComponentsModule</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#50FA7B">    MarkdownModule</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">forChild</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">  ]</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span></code></pre><p>Finally, we could add a markdown directive to our template <code>blog view.component.html</code> which will automatically load and display the content of the markdown file, told you it’s super easy 😂</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> markdown</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">[src]="'./assets/blog/blog.md'">&#x3C;/div></span></span></code></pre><p>As you could notice, the directive reference the <code>blog.md</code> file which just consists of the list of entries (one title, subtitle and author per blog post) and relative urls which will be used for the navigation</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">##  [Title](</span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2">blog</span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2">post</span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2">name</span><span style="color:#FF79C6">-of-</span><span style="color:#F8F8F2">the</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">article)</span></span>
<span class="line"><span style="color:#F8F8F2">### [Subtitle](</span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2">blog</span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2">post</span><span style="color:#FF79C6">/</span><span style="color:#F8F8F2">name</span><span style="color:#FF79C6">-of-</span><span style="color:#F8F8F2">the</span><span style="color:#FF79C6">-</span><span style="color:#F8F8F2">article)</span></span>
<span class="line"><span style="color:#F8F8F2">Posted by [David](mailto:david@fluster.io) on September </span><span style="color:#BD93F9">6</span><span style="color:#F8F8F2">, </span><span style="color:#BD93F9">2018</span></span></code></pre><h3>Post</h3>
<p>Now that our <code>blog-view</code> is ready, we could add our <code>blog-post-view</code>. As we did previously we need to add <code>MarkdownModule.forChild()</code> to the module but we also need to define the parameter of the route (‘:id’) which will allow us to retrieve the post to load, because here comes the trick: **the post route take as parameter the name of the post we want to display respectively the name of the markdown file to load. **So let’s say in our example that we want to display the blog post <code>assets &gt; blog &gt; post &gt; name-of-the-article.md</code> our route will look like <code>/blog/post/name-of-the-article</code></p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">NgModule</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  declarations</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#50FA7B;font-style:italic">BlogPostViewComponent</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">  imports</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    CommonModule</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#50FA7B">    RouterModule</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">forChild</span><span style="color:#F8F8F2">([</span></span>
<span class="line"><span style="color:#F8F8F2">      { path</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> ''</span><span style="color:#F8F8F2">, component</span><span style="color:#FF79C6">:</span><span style="color:#50FA7B;font-style:italic"> BlogPostViewComponent</span><span style="color:#F8F8F2">},</span></span>
<span class="line"><span style="color:#F8F8F2">      { path</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">:id</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">, component</span><span style="color:#FF79C6">:</span><span style="color:#50FA7B;font-style:italic"> BlogPostViewComponent</span><span style="color:#F8F8F2">, pathMatch</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">full</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">}</span></span>
<span class="line"><span style="color:#F8F8F2">    ]),</span></span>
<span class="line"><span style="color:#50FA7B;font-style:italic">    ComponentsModule</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#50FA7B">    MarkdownModule</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">forChild</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2">  ]</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span></code></pre><p>Once done, we could again add the directive to our template, this time we are not going to reference a particular file but rather use a variable name <code>post</code></p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FF79C6">div</span><span style="color:#50FA7B;font-style:italic"> markdown</span><span> </span><span style="color:#FF5555;font-style:italic;text-decoration:underline">[src]="post">&#x3C;/div></span></span></code></pre><p>I guess you understood, since we are using a route parameter, we have to<br />
initialize this variable using the interface <code>ActivatedRoute</code></p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  selector</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-blog-post-view</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">  styleUrls</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">./blog-post-view.component.scss</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">  templateUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./blog-post-view.component.html</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> BlogPostViewComponent</span><span style="color:#FF79C6"> implements</span><span style="color:#8BE9FD;font-style:italic"> OnInit</span><span style="color:#F8F8F2">, </span><span style="color:#8BE9FD;font-style:italic">OnDestroy</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  private</span><span style="color:#F8F8F2"> sub</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Subscription</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  post</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  constructor</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">private</span><span style="color:#FFB86C;font-style:italic"> route</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> ActivatedRoute</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  ngOnInit</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">    this</span><span style="color:#F8F8F2">.sub </span><span style="color:#FF79C6">=</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.route.params.</span><span style="color:#50FA7B">subscribe</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">params</span><span style="color:#FF79C6"> =></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">      this</span><span style="color:#F8F8F2">.post </span><span style="color:#FF79C6">=</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./assets/blog/post/</span><span style="color:#E9F284">'</span><span style="color:#FF79C6"> +</span><span style="color:#F8F8F2">  params[</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">id</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">] </span><span style="color:#FF79C6">+</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">.md</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    });</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  ngOnDestroy</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.sub) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">      this</span><span style="color:#F8F8F2">.sub.</span><span style="color:#50FA7B">unsubscribe</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Et voilà, that’s it, nothing more nothing left in order to route, load and display our blog 👍</p>
<p>Of course the solution would probably need a bit of styling, I won’t cover that in this article but if you are using <a href="https://getbootstrap.com/" rel="noopener noreferrer external">Bootstrap</a> you could for example have a look to this [free clean blog theme (https://startbootstrap.com/template-overviews/clean-blog/)</p>
<p>Once implemented and styled our blog could look like the one I have implemented 👉 <a href="https://fluster.io/blog" rel="noopener noreferrer external">https://fluster.io/blog</a></p>
<h3>Cherry on the cake 🍒🎂</h3>
<p>I realized it afterwards, but you know what’s the cherry on the cake of this solution? You could easily export you Medium stories as markdown (for example with this [Chrome extension](https://chrome.google.com/webstore/detail/convert-medium posts-to-m/aelnflnmpbjgipamcogpdoppjbebnjea)) and therefore integrate them quickly in the blog we just created 🎉</p>
<h3>Bonus hack 🤖</h3>
<p>While developing this blog solution I faced the problem that <code>ngx-markdown</code> was not able to load the markdown files, from the assets folder using relative paths and the <code>HttpClientModule</code> , on the server side of my Angular Universal app which ultimately would have had for effect that such content would have been ignored by crawlers 😢 Prior to Angular v6 I would have solved this by loading resources directly from the filesystem (using<code>fs</code> ) but unfortunately this isn’t possible anymore. Fortunately 😅 I found the following discussion and issue <a href="https://github.com/angular/universal/issues/858" rel="noopener noreferrer external">https://github.com/angular/universal/issues/858</a> which allowed my to solve the problem</p>
<p>In order to help our Angular Universal server to load correctly the resources we need for <code>ngx-markdown</code>, the idea is to intercept all Http requests with the goal to rewrite them with an understandable server side host path. Not rocket science but definitely a must have for our implementation of a friendly SEO blog based on markdown files</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Inject, Injectable, PLATFORM_ID} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/common/http</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {isPlatformServer} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/common</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Observable} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">rxjs</span><span style="color:#E9F284">'</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Injectable</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">  providedIn</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">root</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> HttpInterceptorService</span><span style="color:#FF79C6"> implements</span><span style="color:#8BE9FD;font-style:italic"> HttpInterceptor</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  constructor</span><span style="color:#F8F8F2">(@</span><span style="color:#50FA7B">Inject</span><span style="color:#F8F8F2">(PLATFORM_ID) </span><span style="color:#FF79C6">private</span><span style="color:#FFB86C;font-style:italic"> platformId</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Object</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">  intercept</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">req</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HttpRequest</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">any</span><span style="color:#F8F8F2">>, </span><span style="color:#FFB86C;font-style:italic">next</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> HttpHandler</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Observable</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#FFB86C;font-style:italic">HttpEvent</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">any</span><span style="color:#F8F8F2">>> {</span></span>
<span class="line"><span style="color:#FF79C6">    if</span><span style="color:#F8F8F2"> (</span><span style="color:#50FA7B">isPlatformServer</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.platformId) </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> req.url.</span><span style="color:#50FA7B">includes</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">./</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">)) {</span></span>
<span class="line"><span style="color:#FF79C6">      return</span><span style="color:#F8F8F2"> next.</span><span style="color:#50FA7B">handle</span><span style="color:#F8F8F2">(req.</span><span style="color:#50FA7B">clone</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">        url</span><span style="color:#FF79C6">:</span><span style="color:#F1FA8C"> `http://localhost:8000/</span><span style="color:#FF79C6">${</span><span style="color:#F8F8F2">req.url.</span><span style="color:#50FA7B">replace</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">./</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">, </span><span style="color:#E9F284">''</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">}</span><span style="color:#F1FA8C">`</span></span>
<span class="line"><span style="color:#F8F8F2">      }));</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    return</span><span style="color:#F8F8F2"> next.</span><span style="color:#50FA7B">handle</span><span style="color:#F8F8F2">(req);</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Have fun blogging 🤓</p>
<p>To infinity and beyond 🚀</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        <item>
          <title><![CDATA[Preloading modules in Ionic v4]]></title>
          <description><![CDATA[Preloading modules in Ionic v4]]></description>
          <link>https://daviddalbusco.com/blog/preloading-modules-in-ionic-v4/</link>
          <pubDate>Thu, 23 Aug 2018 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p><img src="https://daviddalbusco.com/assets/images/1*6gmGYZk8Cgx2WNqOMERlLA.jpeg" alt="" / loading="lazy"></p>
<p>I’m currently migrating my mobile application <a href="https://fluster.io/" rel="noopener noreferrer external">Fluster</a> from <a href="https://ionicframework.com/" rel="noopener noreferrer external">Ionic</a> v3 to v4. As you could see in the following video, the very first time a user would access the detail page, he/she would face a small delay 😔 The reason behind this is simple, the remaining (or not loaded yet) modules of the <strong>lazy-loaded Angular</strong> route still have to be loaded</p>
<p><img src="https://daviddalbusco.com/assets/images/1*Uu2ONN9g4VTqkcfzmV5sXw.gif" alt="" / loading="lazy"><br />
<span class="figcaption_hack">A small delay</span></p>
<p>Fortunately, <a href="https://angular.io/" rel="noopener noreferrer external">Angular</a> v6 offers the ability to preload all or selected modules. This has been described many times, like for example in the interesting article of <a href="https://medium.com/@adrianfaciu/custom-preloading-strategy-for-angular-modules-b3b5c873681a" rel="noopener noreferrer external">Adrian Fâciu</a></p>
<p>However, the different solutions provided in all these tutorials weren’t enough or successful for my use case</p>
<h4>Problems</h4>
<p>At first, I tried the out of the box Angular strategy <code>PreloadAllModules</code> which, believe me or not, had a negative effect on my boot time making it slower 😱 which of course I would avoid at any price, a slow boot time being one of my nemesis</p>
<p>I tried then the strategy of preloading only the module of my detail page. Unfortunately, again, the results didn’t match my expectation. The modules were effectively loaded too early regarding my flow or were loaded in the same time as the module of the main page, which again had for effect to slow down my boot time 😢</p>
<p><img src="https://thepracticaldev.s3.amazonaws.com/i/0129tfgjf4i8hgc3mfvv.png" alt="" / loading="lazy"><br />
<span class="figcaption_hack">Without preloading</span></p>
<p><img src="https://thepracticaldev.s3.amazonaws.com/i/bo40x3m3ytmm8j49q43l.png" alt="" / loading="lazy"><br />
<span class="figcaption_hack">With preloading a specific page, the modules were loaded but in the meantime as the others</span></p>
<p>Facing that situation, I then tried the solution described by <a href="https://medium.com/@adrianfaciu" rel="noopener noreferrer external">Adrian Fâciu</a> which worked well but only to some extension. The modules of the detail page were effectively loaded afterwards but I kind of had the feeling that loading modules after a delay was bit “random” for my specific case</p>
<p><img src="https://thepracticaldev.s3.amazonaws.com/i/0y0z0f845mqx67y98wy0.png" alt="" / loading="lazy"><br />
<span class="figcaption_hack">Preloading the modules of the detail page after a delay</span></p>
<h3>Solution</h3>
<p>Fortunately, together with <a href="https://github.com/Aaron-Sterling" rel="noopener noreferrer external">Aaron Sterling</a>, while exchanging on the <a href="https://forum.ionicframework.com/t/v4-preloadmodules/137984" rel="noopener noreferrer external">Ionic Forum</a>, we were able to find a solution which fitted my needs 😃 I so much ❤️ the Ionic community</p>
<p>The idea was to implement a <strong>custom preloading strategy</strong> and to <strong>trigger it only on demand</strong> respectively, in my case, to start the preloading when my main page would be ready</p>
<p><strong>Implementation of the solution</strong></p>
<p>First of all, we need a new provider which will collect the routes we may want to preload and offers the ability to preload a specific route on demand</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {PreloadingStrategy, Route} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/router</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Observable, of} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">rxjs</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#FF79C6">import</span><span style="color:#F8F8F2"> {Injectable} </span><span style="color:#FF79C6">from</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">@angular/core</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> interface</span><span style="color:#8BE9FD;font-style:italic"> RouteToPreload</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">    routePath</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">    route</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Route</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#50FA7B">    load</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Function</span><span style="color:#F8F8F2">;</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Injectable</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    providedIn</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">root</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> AppRoutingPreloaderService</span><span style="color:#FF79C6"> implements</span><span style="color:#8BE9FD;font-style:italic"> PreloadingStrategy</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    private</span><span style="color:#F8F8F2"> routesToPreload</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> RouteToPreload</span><span style="color:#F8F8F2">[] </span><span style="color:#FF79C6">=</span><span style="color:#F8F8F2"> [];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">    constructor</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    preload</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">route</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Route</span><span style="color:#F8F8F2">, </span><span style="color:#50FA7B">load</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Function</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Observable</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">any</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">        if</span><span style="color:#F8F8F2"> (route.data </span><span style="color:#FF79C6">&#x26;&#x26;</span><span style="color:#F8F8F2"> route.data.preload) {</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">            this</span><span style="color:#F8F8F2">.routesToPreload.</span><span style="color:#50FA7B">push</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">                routePath</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> route.path,</span></span>
<span class="line"><span style="color:#F8F8F2">                route</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> route,</span></span>
<span class="line"><span style="color:#F8F8F2">                load</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> load</span></span>
<span class="line"><span style="color:#F8F8F2">            });</span></span>
<span class="line"><span style="color:#F8F8F2">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#50FA7B"> of</span><span style="color:#F8F8F2">(</span><span style="color:#BD93F9">null</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">    preloadRoute</span><span style="color:#F8F8F2">(</span><span style="color:#FFB86C;font-style:italic">path</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> string</span><span style="color:#F8F8F2">)</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">void</span><span style="color:#F8F8F2">> {</span></span>
<span class="line"><span style="color:#FF79C6">        return</span><span style="color:#FF79C6;font-weight:bold"> new</span><span style="color:#8BE9FD;font-style:italic"> Promise</span><span style="color:#F8F8F2">&#x3C;</span><span style="color:#8BE9FD;font-style:italic">void</span><span style="color:#F8F8F2">>((</span><span style="color:#FFB86C;font-style:italic">resolve</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#FF79C6">            if</span><span style="color:#F8F8F2"> (</span><span style="color:#BD93F9;font-style:italic">this</span><span style="color:#F8F8F2">.routesToPreload </span><span style="color:#FF79C6">&#x26;&#x26;</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">                this</span><span style="color:#F8F8F2">.routesToPreload.length </span><span style="color:#FF79C6">></span><span style="color:#BD93F9"> 0</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#FF79C6">                const</span><span style="color:#F8F8F2"> routeToPreload</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> RouteToPreload</span><span style="color:#FF79C6"> =</span></span>
<span class="line"><span style="color:#BD93F9;font-style:italic">                    this</span><span style="color:#F8F8F2">.routesToPreload.</span><span style="color:#50FA7B">find</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#F8F8F2">                       (</span><span style="color:#FFB86C;font-style:italic">filterRouteToPreload</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> RouteToPreload</span><span style="color:#F8F8F2">) </span><span style="color:#FF79C6">=></span></span>
<span class="line"><span style="color:#F8F8F2">                       filterRouteToPreload.routePath </span><span style="color:#FF79C6">===</span><span style="color:#F8F8F2"> path);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">                if</span><span style="color:#F8F8F2"> (routeToPreload) {</span></span>
<span class="line"><span style="color:#F8F8F2">                    routeToPreload.</span><span style="color:#50FA7B">load</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">                }</span></span>
<span class="line"><span style="color:#F8F8F2">            }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#50FA7B">            resolve</span><span style="color:#F8F8F2">();</span></span>
<span class="line"><span style="color:#F8F8F2">        });</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p><em>Note: currently the provider may be loaded twice and therefore collect twice the routes. This isn’t such a problem here because we don’t preload anything per default and because the effective preload will take care of only loading the first route which have been collected</em></p>
<p>Then, as described in other articles, we could modify our <code>app-routing.module.ts</code> in order to provide our custom preloading strategy and to specify which routes are candidates for the preloading</p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#FF79C6">const</span><span style="color:#F8F8F2"> routes</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> Routes</span><span style="color:#FF79C6"> =</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#F8F8F2">     {</span></span>
<span class="line"><span style="color:#F8F8F2">      path</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">main</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">      loadChildren</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./pages/main.module#MainPageModule</span><span style="color:#E9F284">'</span></span>
<span class="line"><span style="color:#F8F8F2">    },</span></span>
<span class="line"><span style="color:#F8F8F2">    {</span></span>
<span class="line"><span style="color:#F8F8F2">      path</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">detail</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">      loadChildren</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./pages/detail.module#DetailPageModule</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">      data</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> {preload</span><span style="color:#FF79C6">:</span><span style="color:#BD93F9"> true</span><span style="color:#F8F8F2">}</span></span>
<span class="line"><span style="color:#F8F8F2">    }</span></span>
<span class="line"><span style="color:#F8F8F2">];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">NgModule</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    imports</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#50FA7B">RouterModule</span><span style="color:#F8F8F2">.</span><span style="color:#50FA7B">forRoot</span><span style="color:#F8F8F2">(</span><span style="color:#50FA7B;font-style:italic">routes</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">                 {preloadingStrategy</span><span style="color:#FF79C6">:</span><span style="color:#50FA7B;font-style:italic"> AppRoutingPreloaderService</span><span style="color:#F8F8F2">}</span></span>
<span class="line"><span style="color:#F8F8F2">             )],</span></span>
<span class="line"><span style="color:#F8F8F2">    exports</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#50FA7B;font-style:italic">RouterModule</span><span style="color:#F8F8F2">]</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> AppRoutingModule</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Finally, let the magic happens ✨, we could use the provider in our selected page where we would like to trigger the preloading on demand, for example starting at the lifecycle of our choice like <code>ionViewDidEnter</code> or <code>afterViewChecked</code></p>
<pre class="shiki dracula" style="background-color:#000000;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F8F8F2">@</span><span style="color:#50FA7B">Component</span><span style="color:#F8F8F2">({</span></span>
<span class="line"><span style="color:#F8F8F2">    selector</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">app-main</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    templateUrl</span><span style="color:#FF79C6">:</span><span style="color:#E9F284"> '</span><span style="color:#F1FA8C">./main.page.html</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2">    styleUrls</span><span style="color:#FF79C6">:</span><span style="color:#F8F8F2"> [</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">./main.page.scss</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">],</span></span>
<span class="line"><span style="color:#F8F8F2">})</span></span>
<span class="line"><span style="color:#FF79C6">export</span><span style="color:#FF79C6"> class</span><span style="color:#8BE9FD"> ItemsPage</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"></span>
<span class="line"><span style="color:#FF79C6">  constructor</span><span style="color:#F8F8F2">(</span><span style="color:#FF79C6">private</span><span style="color:#FFB86C;font-style:italic"> routingService</span><span style="color:#FF79C6">:</span><span style="color:#8BE9FD;font-style:italic"> AppRoutingPreloaderService</span><span style="color:#F8F8F2">) {}</span></span>
<span class="line"><span style="color:#FF79C6">  async</span><span style="color:#50FA7B"> ionViewDidEnter</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#FF79C6">    await</span><span style="color:#BD93F9;font-style:italic"> this</span><span style="color:#F8F8F2">.routingService.</span><span style="color:#50FA7B">preloadRoute</span><span style="color:#F8F8F2">(</span><span style="color:#E9F284">'</span><span style="color:#F1FA8C">details</span><span style="color:#E9F284">'</span><span style="color:#F8F8F2">);</span></span>
<span class="line"><span style="color:#F8F8F2">  }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span></code></pre><p>Doing so, as you could see in the next screenshot, the modules of my detail page are preloaded smoothly after my app as successfully booted and after the main page has been displayed and being set has ready for interactions</p>
<p><img src="https://thepracticaldev.s3.amazonaws.com/i/ad3jufcxts4y8b9iig81.png" alt="" / loading="lazy"><br />
<span class="figcaption_hack">Preloading the modules of the detail page after the main page did entered</span></p>
<p>Voilà ✌️The solution still need some more testing on real devices but I’m confident that from now on, when my users will use my Ionic v4 app and will access the detail page for the very first time, they will face a slighter smaller delay or even almost none 😉</p>
<p>I hope this solution could helps you a bit too if you would like to preload some routes or pages in your awesome app 😁</p>
<p>To infinity and beyond 🚀</p>
<p>David</p>
]]></content:encoded>
        </item>
      
        </channel>
    </rss>