<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Tutorials on GeekyHub</title>
    <link>https://www.geekyhub.in/tags/tutorials/</link>
    <description>Recent content in Tutorials on GeekyHub</description>
    <generator>Hugo -- 0.148.1</generator>
    <language>en</language>
    <lastBuildDate>Mon, 02 Sep 2024 17:17:05 +0530</lastBuildDate>
    <atom:link href="https://www.geekyhub.in/tags/tutorials/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>How to Use Tiptap&#39;s Collaboration Feature with Rails Action Cable</title>
      <link>https://www.geekyhub.in/post/implementing-a-google-doc-notion-like-collborative-editor-in-rails-react-tiptap/</link>
      <pubDate>Tue, 18 Jun 2024 20:55:00 +0530</pubDate>
      <guid>https://www.geekyhub.in/post/implementing-a-google-doc-notion-like-collborative-editor-in-rails-react-tiptap/</guid>
      <description>Create a collaborative text editor like Notion using Tiptap for rich text, ReactJS for frontend, and Rails with Action Cable for real-time updates. Enable simultaneous editing and instant synchronization across users for a seamless collaborative experience.</description>
      <content:encoded><![CDATA[<p>In this post, we&rsquo;ll walk through setting up Tiptap&rsquo;s collaboration feature with Rails Action Cable and ReactJs. Tiptap is a powerful headless editor built on ProseMirror, and when combined with Y.js, it allows for real-time collaborative editing. We&rsquo;ll use Mantine component library, but it&rsquo;s not mandatory for this setup.</p>
<p>If you prefer to dive directly into the code, check out the example on <a href="https://github.com/vikas-0/collab_demo">Github</a></p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube-nocookie.com/embed/HXpudWU5FxQ?autoplay=0&amp;controls=1&amp;end=0&amp;loop=1&amp;mute=0&amp;playlist=HXpudWU5FxQ&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<h3 id="prerequisites">Prerequisites</h3>
<p>Ensure you have the following installed:</p>
<ul>
<li>Ruby on Rails</li>
<li>Redis</li>
<li>Node.js and Yarn</li>
<li>Your preferred mehtod of React Setup with Rails</li>
</ul>
<h3 id="step-1-setting-up-mantine">Step 1: Setting Up Mantine</h3>
<p>First, we&rsquo;ll set up Mantine for styling. Follow the <a href="https://mantine.dev/guides/vite/">Mantine guide for Vite</a> to install the necessary packages: (Same method worked for me using esbuild in my setup. You can do it you own way or choose not to use Mantine)</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>yarn add @mantine/core @mantine/hooks @mantine/tiptap @tabler/icons-react @tiptap/react @tiptap/extension-link @tiptap/starter-kit @tiptap/extension-placeholder @tiptap/extension-collaboration-cursor @tiptap/extension-collaboration yjs y-prosemirror
</span></span><span style="display:flex;"><span>yarn add --dev postcss postcss-preset-mantine postcss-simple-vars
</span></span></code></pre></div><blockquote>
<p>Note: This setup includes both Mantine and Tiptap. If you do not require Mantine, skip installing Mantine-related dependencies.</p></blockquote>
<h3 id="step-2-install-rails-dependencies">Step 2: Install Rails Dependencies</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>bundle add redis y-rb_actioncable y-rb
</span></span></code></pre></div><p>Here we are installing Y.js adapter for Ruby and Action Cable.</p>
<h3 id="step-3-configure-tiptap-with-collaboration">Step 3: Configure Tiptap with Collaboration</h3>
<p>In the Tiptap setup, configure the StarterKit with history: false as the Collaboration extension comes with its own history management. Additionally, we’ll add a random color generator for collaboration cursors.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">getRandomColor</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">colors</span> <span style="color:#f92672">=</span> [<span style="color:#e6db74">&#34;#ff901f&#34;</span>, <span style="color:#e6db74">&#34;#ff2975&#34;</span>, <span style="color:#e6db74">&#34;#f222ff&#34;</span>, <span style="color:#e6db74">&#34;#8c1eff&#34;</span>];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">selectedIndex</span> <span style="color:#f92672">=</span> Math.<span style="color:#a6e22e">floor</span>(Math.<span style="color:#a6e22e">random</span>() <span style="color:#f92672">*</span> (<span style="color:#a6e22e">colors</span>.<span style="color:#a6e22e">length</span> <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>));
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">colors</span>[<span style="color:#a6e22e">selectedIndex</span>];
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">editor</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">useEditor</span>({
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">extensions</span><span style="color:#f92672">:</span> [
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">StarterKit</span>.<span style="color:#a6e22e">configure</span>({ <span style="color:#a6e22e">history</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">false</span> }),
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">Underline</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">Link</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">Superscript</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">SubScript</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">Highlight</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">TextAlign</span>.<span style="color:#a6e22e">configure</span>({ <span style="color:#a6e22e">types</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">&#39;heading&#39;</span>, <span style="color:#e6db74">&#39;paragraph&#39;</span>] }),
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">Placeholder</span>.<span style="color:#a6e22e">configure</span>({ <span style="color:#a6e22e">placeholder</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;This is placeholder&#39;</span> }),
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">Collaboration</span>.<span style="color:#a6e22e">configure</span>({
</span></span><span style="display:flex;"><span>                document<span style="color:#f92672">:</span> <span style="color:#a6e22e">doc</span> <span style="color:#75715e">// Configure Y.Doc for collaboration
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>            }),
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">CollaborationCursor</span>.<span style="color:#a6e22e">configure</span>({
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">provider</span>,
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">user</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>                    <span style="color:#a6e22e">name</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;Vikas&#34;</span>,
</span></span><span style="display:flex;"><span>                    <span style="color:#a6e22e">color</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">getRandomColor</span>()
</span></span><span style="display:flex;"><span>                }
</span></span><span style="display:flex;"><span>            })
</span></span><span style="display:flex;"><span>        ]
</span></span><span style="display:flex;"><span>    });
</span></span></code></pre></div><p>Code to connect with websocket provided by ActionCable. Don&rsquo;t worry about the channel creation now, we will create it later. Assuming channel name will be <code>SyncChannel</code> we will add following code. (Here id is hardcoded, as this is just a demo. we won&rsquo;t be using proper auth in backend as well to keep things simple)</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#75715e">// ... other imports
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">createConsumer</span> } <span style="color:#a6e22e">from</span> <span style="color:#e6db74">&#34;@rails/actioncable&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">WebsocketProvider</span> } <span style="color:#a6e22e">from</span> <span style="color:#e6db74">&#34;@y-rb/actioncable&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">consumer</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">createConsumer</span>();
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">doc</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Y</span>.<span style="color:#a6e22e">Doc</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">provider</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">WebsocketProvider</span>(
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">doc</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">consumer</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;SyncChannel&#34;</span>,
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">id</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// ... other codes
</span></span></span></code></pre></div><p>You can see full frontend code in <a href="https://github.com/vikas-0/collab_demo/blob/main/app/javascript/App.jsx">App.jsx</a>. This contains everything in a single file which is not great but good enough for this case.</p>
<h3 id="step-4-set-up-rails-action-cable">Step 4: Set Up Rails Action Cable</h3>
<p>Create a new channel name <code>SyncChannel</code> at <code>app/channels/sync_channel.rb</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#75715e"># frozen_string_literal: true</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">SyncChannel</span> <span style="color:#f92672">&lt;</span> <span style="color:#66d9ef">ApplicationCable</span><span style="color:#f92672">::</span><span style="color:#66d9ef">Channel</span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">include</span> Y<span style="color:#f92672">::</span><span style="color:#66d9ef">Actioncable</span><span style="color:#f92672">::</span><span style="color:#66d9ef">Sync</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">subscribed</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># initiate sync &amp; subscribe to updates, with optional persistence mechanism</span>
</span></span><span style="display:flex;"><span>    sync_for(session) { <span style="color:#f92672">|</span>id, update<span style="color:#f92672">|</span> save_doc(id, update) }
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">receive</span>(message)
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># broadcast update to all connected clients on all servers</span>
</span></span><span style="display:flex;"><span>    sync_to(session, message)
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">doc</span>
</span></span><span style="display:flex;"><span>    @doc <span style="color:#f92672">||=</span> load { <span style="color:#f92672">|</span>id<span style="color:#f92672">|</span> load_doc(id) }
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">private</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">session</span>
</span></span><span style="display:flex;"><span>    @session <span style="color:#f92672">||=</span> <span style="color:#66d9ef">Session</span><span style="color:#f92672">.</span>new(params<span style="color:#f92672">[</span><span style="color:#e6db74">:id</span><span style="color:#f92672">]</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">load_doc</span>(id)
</span></span><span style="display:flex;"><span>    data <span style="color:#f92672">=</span> <span style="color:#66d9ef">REDIS</span><span style="color:#f92672">.</span>get(id)
</span></span><span style="display:flex;"><span>    data <span style="color:#f92672">=</span> data<span style="color:#f92672">.</span>unpack(<span style="color:#e6db74">&#34;C*&#34;</span>) <span style="color:#66d9ef">unless</span> data<span style="color:#f92672">.</span>nil?
</span></span><span style="display:flex;"><span>    data
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">save_doc</span>(id, state)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">REDIS</span><span style="color:#f92672">.</span>set(id, state<span style="color:#f92672">.</span>pack(<span style="color:#e6db74">&#34;C*&#34;</span>))
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">end</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">end</span>
</span></span></code></pre></div><p>This has Redis initialized as REDIS, replace it with your Redis variable name. We also created a Session model for <code>sync_for</code> mehtod. You can check documentation for sync_for <a href="https://y-crdt.github.io/yrb-actioncable/Y/Actioncable/Sync.html#sync_for-instance_method">here</a>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#75715e"># frozen_string_literal: true</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Session</span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">attr_reader</span> <span style="color:#e6db74">:id</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">initialize</span>(id)
</span></span><span style="display:flex;"><span>    @id <span style="color:#f92672">=</span> id
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">to_s</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;sessions:</span><span style="color:#e6db74">#{</span>id<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">end</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">end</span>
</span></span></code></pre></div><p>And finally <code>ApplicationCable::Connection</code> will be as follows</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#66d9ef">module</span> ApplicationCable
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Connection</span> <span style="color:#f92672">&lt;</span> <span style="color:#66d9ef">ActionCable</span><span style="color:#f92672">::</span><span style="color:#66d9ef">Connection</span><span style="color:#f92672">::</span><span style="color:#66d9ef">Base</span>
</span></span><span style="display:flex;"><span>    identified_by <span style="color:#e6db74">:id</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">connect</span>
</span></span><span style="display:flex;"><span>      self<span style="color:#f92672">.</span>id <span style="color:#f92672">=</span> <span style="color:#66d9ef">SecureRandom</span><span style="color:#f92672">.</span>uuid
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">end</span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">end</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">end</span>
</span></span></code></pre></div><h3 id="step-6-add-styles-for-collaboration-cursor-option">Step 6: Add Styles for Collaboration Cursor (Option)</h3>
<p>Everything should be working by now. In this step the cursor was looking odd, so some <a href="https://github.com/vikas-0/collab_demo/blob/main/app/javascript/App.css">CSS</a> can be add to make it look good.</p>
<p>Finally you can run your rails server and it should be good to go once we add all missing piecies specially authorisation.</p>
<h3 id="conclusion">Conclusion</h3>
<p>By following these steps, you should have a real-time collaborative editor up and running using Tiptap, Y.js, and Rails Action Cable. While we used Mantine for styling in this demo, you can customize the styling as per your requirements. This setup provides a robust foundation for building collaborative applications with rich text editing capabilities.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Seamless Remote Development with Any IDE Using Unison for Bidirectional SSH Sync</title>
      <link>https://www.geekyhub.in/post/seamless-remote-development-with-any-ide-using-unison-for-bidirectional-ssh-sync/</link>
      <pubDate>Sat, 15 Jun 2024 11:51:00 +0530</pubDate>
      <guid>https://www.geekyhub.in/post/seamless-remote-development-with-any-ide-using-unison-for-bidirectional-ssh-sync/</guid>
      <description>SSH Development with Any Editor, Unison helps maintain a local copy and enables bidirectional sync, allowing you to use any tool, even Notepad, to edit your code efficiently.</description>
      <content:encoded><![CDATA[<p>If your development environment is on a remote server, you might need to code on the remote machine itself to see the effects of your changes in real time. This scenario is fairly common these days as applications become more complex, and setting up a local development environment requires extensive configuration or mocking of the resources used in the app.</p>
<p>In this case, you are left with very few options for code editors. Either you have to use CLI-based editors such as Vim, or you can use VSCode with the <a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-ssh">Remote SSH extension</a>. Even with the extension, there are some issues related to code analysis and code completion. These processes have to happen on the remote machine, making your powerful local PC underutilized. And if you want to use Zed or Xcode, you are out of luck.</p>
<p>So, the solution is to have a local copy that is synced with the remote SSH directory. You can use several tools for this:</p>
<ul>
<li>
<p><strong>Rsync</strong>:</p>
<ul>
<li>Unidirectional sync, which might be fine for development as you will use the local machine&rsquo;s copy to manage changes on the SSH server.</li>
<li>Does not support file deletion.</li>
</ul>
</li>
<li>
<p><strong>SSHFS</strong>:</p>
<ul>
<li>Limited support on macOS.</li>
<li>Requires kernel changes, making it impractical for use on some machines.</li>
</ul>
</li>
<li>
<p><strong>Unison</strong>:</p>
<ul>
<li>Supports bidirectional sync.</li>
<li>Handles file deletions.</li>
<li>Fault-tolerant: even if the connection breaks, you can restart it, and it will continue syncing from the last known point.</li>
</ul>
</li>
</ul>
<p>This leaves Unison as the ideal choice for me.</p>
<p><a href="https://github.com/bcpierce00/unison">Unison</a> has fairly good documentation; you can go ahead and check their documents and explore on your own.</p>
<h1 id="installing-unison">Installing Unison</h1>
<p>You need to install Unison on both the host and remote machine. If you don&rsquo;t have sudo access on the remote machine, don&rsquo;t worry; you can download the binary and keep it anywhere.</p>
<ul>
<li>
<p><strong>Installing Unison on Mac local machine:</strong></p>
<ul>
<li><code>brew install unison</code></li>
<li><code>brew install autozimu/homebrew-formulas/unison-fsmonitor</code></li>
</ul>
</li>
<li>
<p>For Linux, you can follow the <a href="https://www.cis.upenn.edu/~bcpierce/unison/download/releases/stable/">official guide</a>.</p>
</li>
<li>
<p>Make sure to create a local folder to sync code from the remote machine.</p>
</li>
<li>
<p><strong>Run a test connection:</strong></p>
<ul>
<li>Example command: <code>unison -testServer ssh://user@remotehostname/path/to/folder /path/to/folder</code></li>
<li>If you get an error that Unison is not found on the remote server, specify the exact path to Unison using <code>-servercmd</code>. For example: <code>unison -testServer ssh://user@remotehostname/path/to/folder /path/to/folder -servercmd /home/path/to/unison/bin/unison</code>.</li>
</ul>
</li>
<li>
<p><strong>Final command for syncing:</strong></p>
<ul>
<li>Once the connection test is successful, start syncing with the final command:
<pre tabindex="0"><code>unison ssh://user@remotehostname/path/to/folder /path/to/folder -servercmd /home/path/to/unison/bin/unison -ignore &#34;BelowPath node_modules&#34; -ignore &#34;BelowPath .git&#34; -force newer -repeat watch
</code></pre></li>
<li>This command will sync all files and continue monitoring for further changes in the background while you develop. If you want to know the meaning of flags used here like <code>ignore</code>, <code>force</code> etc, you can check <a href="https://github.com/bcpierce00/unison/wiki">Unison user mannual</a>.</li>
</ul>
</li>
</ul>
<p>You can use any code editor to modify the local copy, and Unison will automatically sync the changes.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Animate SVG in React Native</title>
      <link>https://www.geekyhub.in/post/animate-svg-using-react-native-renanimated-2/</link>
      <pubDate>Sat, 05 Feb 2022 11:00:00 +0530</pubDate>
      <guid>https://www.geekyhub.in/post/animate-svg-using-react-native-renanimated-2/</guid>
      <description>Animating SVG in React-Native using react-native-reanimated.</description>
      <content:encoded><![CDATA[<p>Here, by animating SVG, I mean to change the property of SVG elements dynamically, which will look live-like.</p>
<p>In react native, we can generate/render an SVG using the <a href="https://github.com/react-native-svg/react-native-svg">react-native-svg</a> library. A complex SVG comprises many more minor elements that could be animated individually. But here, for example, we will take only one piece, a circle.</p>
<p>The following code will draw a circle with a radius of 50 units.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-react" data-lang="react"><span style="display:flex;"><span>&lt;<span style="color:#f92672">Svg</span> <span style="color:#a6e22e">width</span><span style="color:#f92672">=</span>{<span style="color:#ae81ff">200</span>} <span style="color:#a6e22e">height</span><span style="color:#f92672">=</span>{<span style="color:#ae81ff">200</span>}&gt;
</span></span><span style="display:flex;"><span>  &lt;<span style="color:#f92672">Circle</span> <span style="color:#a6e22e">cx</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;55&#34;</span> <span style="color:#a6e22e">cy</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;55&#34;</span> <span style="color:#a6e22e">r</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;50&#34;</span> <span style="color:#a6e22e">stroke</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;black&#34;</span> <span style="color:#a6e22e">strokeWidth</span><span style="color:#f92672">=</span>{<span style="color:#ae81ff">5</span>} /&gt;
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#f92672">Svg</span>&gt;
</span></span></code></pre></div><p>Suppose we want to animate it to become large and small.
To achieve this, I will use <a href="https://docs.swmansion.com/react-native-reanimated/">React Native Reanimated</a>. To learn more about it, you can check out its documentation.</p>
<p>Logically, I am trying to increase or decrease the radius.</p>
<p>Since the radius value is a prop, I will use <code>useAnimatedProps</code>. But, first of all, convert Circle to an animated component.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-react" data-lang="react"><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">AnimatedCircle</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">Animated</span>.<span style="color:#a6e22e">createAnimatedComponent</span>(<span style="color:#a6e22e">Circle</span>);
</span></span></code></pre></div><p>Now, I can re-write component as</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-react" data-lang="react"><span style="display:flex;"><span>&lt;<span style="color:#f92672">Svg</span> <span style="color:#a6e22e">width</span><span style="color:#f92672">=</span>{<span style="color:#ae81ff">200</span>} <span style="color:#a6e22e">height</span><span style="color:#f92672">=</span>{<span style="color:#ae81ff">200</span>}&gt;
</span></span><span style="display:flex;"><span>  &lt;<span style="color:#f92672">AnimatedCircle</span> <span style="color:#a6e22e">cx</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;55&#34;</span> <span style="color:#a6e22e">cy</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;55&#34;</span> <span style="color:#a6e22e">r</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;50&#34;</span> <span style="color:#a6e22e">stroke</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;black&#34;</span> <span style="color:#a6e22e">strokeWidth</span><span style="color:#f92672">=</span>{<span style="color:#ae81ff">5</span>} /&gt;
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#f92672">Svg</span>&gt;
</span></span></code></pre></div><p>Next step is to store the stroke width and radius. width can be stored in a simple constat, but for the radius we will use <a href="https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/shared-values"><code>useSharedValue</code></a>, so that it can be used by woklet to animate.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-react" data-lang="react"><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">default</span> ()=&gt;{
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">radius</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">useSharedValue</span>(<span style="color:#ae81ff">50</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">strokeWidth</span> <span style="color:#f92672">=</span> <span style="color:#ae81ff">5</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> (
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">Svg</span> <span style="color:#a6e22e">width</span><span style="color:#f92672">=</span>{<span style="color:#ae81ff">200</span>} <span style="color:#a6e22e">height</span><span style="color:#f92672">=</span>{<span style="color:#ae81ff">200</span>}&gt;
</span></span><span style="display:flex;"><span>      &lt;<span style="color:#f92672">AnimatedCircle</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">cx</span><span style="color:#f92672">=</span>{<span style="color:#e6db74">`</span><span style="color:#e6db74">${</span><span style="color:#a6e22e">radius</span>.<span style="color:#a6e22e">value</span> <span style="color:#f92672">+</span> <span style="color:#a6e22e">strokeWidth</span><span style="color:#e6db74">}</span><span style="color:#e6db74">`</span>}
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">cy</span><span style="color:#f92672">=</span>{<span style="color:#e6db74">`</span><span style="color:#e6db74">${</span><span style="color:#a6e22e">radius</span>.<span style="color:#a6e22e">value</span> <span style="color:#f92672">+</span> <span style="color:#a6e22e">strokeWidth</span><span style="color:#e6db74">}</span><span style="color:#e6db74">`</span>}
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">r</span><span style="color:#f92672">=</span>{<span style="color:#e6db74">`</span><span style="color:#e6db74">${</span><span style="color:#a6e22e">radius</span>.<span style="color:#a6e22e">value</span><span style="color:#e6db74">}</span><span style="color:#e6db74">`</span>}
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">stroke</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;black&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">strokeWidth</span><span style="color:#f92672">=</span>{<span style="color:#a6e22e">strokeWidth</span>}
</span></span><span style="display:flex;"><span>      /&gt;
</span></span><span style="display:flex;"><span>    &lt;/<span style="color:#f92672">Svg</span>&gt;
</span></span><span style="display:flex;"><span>  )
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Here, I need some event or action to trigger the radius change. I&rsquo;ll use a button press. (withSpring is default provided animation which is not necessary to use but this looks cool 🤞)</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-react" data-lang="react"><span style="display:flex;"><span>&lt;<span style="color:#f92672">Button</span> <span style="color:#a6e22e">mode</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;contained&#34;</span> <span style="color:#a6e22e">onPress</span><span style="color:#f92672">=</span>{() =&gt; {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">if</span>(<span style="color:#a6e22e">radius</span>.<span style="color:#a6e22e">value</span> <span style="color:#f92672">&lt;</span> <span style="color:#ae81ff">80</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">radius</span>.<span style="color:#a6e22e">value</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">withSpring</span>(<span style="color:#ae81ff">80</span>)
</span></span><span style="display:flex;"><span>  }<span style="color:#66d9ef">else</span>{
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">radius</span>.<span style="color:#a6e22e">value</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">withSpring</span>(<span style="color:#ae81ff">50</span>)
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}}&gt;
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">Press</span>
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#f92672">Button</span>&gt;
</span></span></code></pre></div><p>You&rsquo;ll notice that even after pressing the button, nothing happens. It is because change of sharedValue doesn&rsquo;t trigger re-render of the react component. finally animated props comes into the picture. Instead of passing prop directly, we will pass it using animated prop.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-react" data-lang="react"><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">animatedProps</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">useAnimatedProps</span>(() =&gt; ({
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">cx</span><span style="color:#f92672">:</span> <span style="color:#e6db74">`</span><span style="color:#e6db74">${</span><span style="color:#a6e22e">radius</span>.<span style="color:#a6e22e">value</span> <span style="color:#f92672">+</span> <span style="color:#a6e22e">strokeWidth</span><span style="color:#e6db74">}</span><span style="color:#e6db74">`</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">cy</span><span style="color:#f92672">:</span> <span style="color:#e6db74">`</span><span style="color:#e6db74">${</span><span style="color:#a6e22e">radius</span>.<span style="color:#a6e22e">value</span> <span style="color:#f92672">+</span> <span style="color:#a6e22e">strokeWidth</span><span style="color:#e6db74">}</span><span style="color:#e6db74">`</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">r</span><span style="color:#f92672">:</span><span style="color:#e6db74">`</span><span style="color:#e6db74">${</span><span style="color:#a6e22e">radius</span>.<span style="color:#a6e22e">value</span><span style="color:#e6db74">}</span><span style="color:#e6db74">`</span>
</span></span><span style="display:flex;"><span>}));
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-react" data-lang="react"><span style="display:flex;"><span>&lt;<span style="color:#f92672">Svg</span> <span style="color:#a6e22e">width</span><span style="color:#f92672">=</span>{<span style="color:#ae81ff">200</span>} <span style="color:#a6e22e">height</span><span style="color:#f92672">=</span>{<span style="color:#ae81ff">200</span>}&gt;
</span></span><span style="display:flex;"><span>  &lt;<span style="color:#f92672">AnimatedCircle</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">animatedProps</span><span style="color:#f92672">=</span>{<span style="color:#a6e22e">animatedProps</span>}
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">stroke</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;black&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">strokeWidth</span><span style="color:#f92672">=</span>{<span style="color:#a6e22e">strokeWidth</span>}
</span></span><span style="display:flex;"><span>  /&gt;
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#f92672">Svg</span>&gt;
</span></span></code></pre></div><p>Result, is something like this.
<figure>
    <img loading="lazy" src="circle-animation.gif" width="300px"/> 
</figure>
</p>
<p>This is a relatively simple example, but I hope I successfully demonstrated the possibilities.
Here is the <a href="https://gist.github.com/vikas-0/24c785c2a178a790b3b7352b400cc400">link</a> of complete code of the result if you are interested.</p>
]]></content:encoded>
    </item>
    <item>
      <title>How to use OneSignal with ReactJs</title>
      <link>https://www.geekyhub.in/post/using-one-signal-with-react-js/</link>
      <pubDate>Sat, 09 Jan 2021 18:16:00 +0530</pubDate>
      <guid>https://www.geekyhub.in/post/using-one-signal-with-react-js/</guid>
      <description>Exploring the possiblity to use OneSignal with ReactJs with existing service worker</description>
      <content:encoded><![CDATA[<p>Using OneSignal with react is fairly easy if one doesn&rsquo;t care about existing service-worker. But if there is an existing service worker, things may not work as is expected.</p>
<p>First, we will create a react app with a service-worker.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>npx create-react-app my-app --template pwa
</span></span></code></pre></div><p>This should create a folder named <code>'my-app'</code> containing all the app-related files. To check everything is fine, we can run the following commands, and a browser tab should open with the ReactJs logo.
<figure>
    <img loading="lazy" src="cra-app-homepage.png"/> 
</figure>
</p>
<p>Now we will enable the default service worker.</p>
<p>In <code>src/index.js</code> change <code>unregister()</code> to <code>register()</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#75715e">// serviceWorkerRegistration.unregister();
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#a6e22e">serviceWorkerRegistration</span>.<span style="color:#a6e22e">register</span>();
</span></span></code></pre></div><p>Let us add some code in the service worker (<code>src/service-worker.js</code>) to send a test push message from Chrome DevTools.</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">72
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">73
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">74
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">75
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">76
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#75715e">// Any other custom service worker logic can go here.
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">self</span>.<span style="color:#a6e22e">addEventListener</span>(<span style="color:#e6db74">&#39;push&#39;</span>, (<span style="color:#a6e22e">event</span>) =&gt; {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#a6e22e">event</span>.<span style="color:#a6e22e">data</span>);
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></td></tr></table>
</div>
</div><p>But wait! the service worker will not run in the dev server by default. We can change this behavior, but it&rsquo;s not recommended to do so. It should be built first and then served. To serve, we need need to install a static file server such as <code>serve</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>npm install -g serve
</span></span></code></pre></div><p>Then build and serve by running following commands form <code>my-app</code> folder.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>npm run build
</span></span><span style="display:flex;"><span>serve -s build
</span></span></code></pre></div><figure>
    <img loading="lazy" src="chromedevtool.png" width="500px"/> <figcaption>
            We can see that the worker is loaded, and the test push message object is logged in the console.
        </figcaption>
</figure>

<h2 id="onesignal-integration">OneSignal Integration</h2>
<p>The first few steps are pretty straightforward. Create an OneSignal account, then create a new website and choose custom code and fill site setup form as shown <em>(Assuming we will be serving on port 5000)</em>.
<figure>
    <img loading="lazy" src="onesignal-setting.png"/> 
</figure>
</p>
<p>Post this step; we&rsquo;ll get a zip file (Web SDK) containing service workers to download. We&rsquo;ll have extract files <code>OneSignalSDKWorker.js</code> , <code>OneSignalSDKUpdaterWorker.js</code> and put them inside the public folder.</p>
<p>And then finally we&rsquo;ll get the custom code.
<figure>
    <img loading="lazy" src="onesignalcustomcode.png"/> 
</figure>
</p>
<p>The first script tag will be placed in <code>public/index.html</code>.</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">39
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">40
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">41
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>    &lt;<span style="color:#f92672">script</span> <span style="color:#a6e22e">src</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;https://cdn.onesignal.com/sdks/OneSignalSDK.js&#34;</span> <span style="color:#a6e22e">async</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;&#34;</span>&gt;&lt;/<span style="color:#f92672">script</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;/<span style="color:#f92672">body</span>&gt;
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#f92672">html</span>&gt;
</span></span></code></pre></td></tr></table>
</div>
</div><p>Second script tag can be put into at the end <code>index.js</code> after few modification for this demo.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span>window.<span style="color:#a6e22e">OneSignal</span> <span style="color:#f92672">=</span> window.<span style="color:#a6e22e">OneSignal</span> <span style="color:#f92672">||</span> [];
</span></span><span style="display:flex;"><span>window.<span style="color:#a6e22e">OneSignal</span>.<span style="color:#a6e22e">push</span>(<span style="color:#66d9ef">function</span>() {
</span></span><span style="display:flex;"><span>  window.<span style="color:#a6e22e">OneSignal</span>.<span style="color:#a6e22e">init</span>({
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">appId</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;eb29dedd-4af2-4fa8-b73a-39bc2e7cc6d5&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">notifyButton</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">enable</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">true</span>,
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">allowLocalhostAsSecureOrigin</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">true</span>,
</span></span><span style="display:flex;"><span>  });
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><p>Two more steps are necessary as, OneSignal service-worker will replace the existing service-worker.</p>
<ol>
<li>We will make OneSignal Service worker as default service-worker by updating path in <code>serviceWorkerRegistration.js</code></li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span>window.<span style="color:#a6e22e">addEventListener</span>(<span style="color:#e6db74">&#39;load&#39;</span>, () =&gt; {
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">// const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">swUrl</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">`</span><span style="color:#e6db74">${</span><span style="color:#a6e22e">process</span>.<span style="color:#a6e22e">env</span>.<span style="color:#a6e22e">PUBLIC_URL</span><span style="color:#e6db74">}</span><span style="color:#e6db74">/OneSignalSDKWorker.js`</span>;
</span></span></code></pre></div><ol start="2">
<li>We&rsquo;ll append <code>importScripts('/service-worker.js');</code> in <code>OneSignalSDKWorker.js</code> and <code>OneSignalSDKUpdaterWorker.js</code>, so that our existing service worker can still be loaded.</li>
</ol>
<p>Now a bell icon will appear at bottom right and clicing on that will intiate subscribe option. Things can be tested through OneSignal dashboard.
<figure>
    <img loading="lazy" src="test-notification.png"/> 
</figure>
</p>
<p>Now the OneSignal worker will be able to show notification, as well as the existing service worker will also log the object in console.</p>
<p>This is probably not the most elegant way and there is obviously scope of improvment in this approach so feel free to comment.</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
