<?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/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Make it Easy]]></title><description><![CDATA[Make it Easy]]></description><link>https://blog.jalaj.dev</link><generator>RSS for Node</generator><lastBuildDate>Sun, 10 May 2026 14:35:58 GMT</lastBuildDate><atom:link href="https://blog.jalaj.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Managing Two GitHub Accounts on Your Mac]]></title><description><![CDATA[If you find yourself juggling between work and personal GitHub accounts on your Mac, you're not alone. This guide will walk you through the steps to set up and manage two separate GitHub accounts with ease. I've tested these steps on MacOS Ventura M1...]]></description><link>https://blog.jalaj.dev/managing-two-github-accounts-on-your-mac</link><guid isPermaLink="true">https://blog.jalaj.dev/managing-two-github-accounts-on-your-mac</guid><category><![CDATA[GitHub]]></category><category><![CDATA[ssh]]></category><category><![CDATA[Git]]></category><dc:creator><![CDATA[Jalaj Gupta]]></dc:creator><pubDate>Wed, 01 Nov 2023 11:19:45 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1698837452077/f6084dea-d2c1-41d4-a4de-a42f831327ef.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you find yourself juggling between work and personal GitHub accounts on your Mac, you're not alone. This guide will walk you through the steps to set up and manage two separate GitHub accounts with ease. I've tested these steps on MacOS Ventura M1, but they should work for most Mac users. If you're using Linux, you may need to adapt the commands accordingly. Apologies to Windows users as this guide is tailored for macOS.</p>
<blockquote>
<p><strong>Quick Tip</strong>: For a summary of the steps, scroll to the bottom for a TL;DR.</p>
</blockquote>
<h2 id="heading-setting-up-ssh-keys">Setting up SSH Keys</h2>
<p>We need to set up two SSH keys, one for work and another one for the personal account. Give them clear names, like <code>jalaj-work-github</code> and <code>jalaj-personal-github</code>.</p>
<p>Here's how you can create SSH keys for both accounts:</p>
<pre><code class="lang-plaintext">ssh-keygen -t rsa -C "jalaj@work.com" -f "jalaj-work-github"
</code></pre>
<p>After running this command, you'll be prompted to enter a passphrase. You can choose something memorable or leave it empty to avoid entering it every time you push or pull.</p>
<p>Similarly, create an SSH key for your personal account:</p>
<pre><code class="lang-plaintext">ssh-keygen -t rsa -C "jalaj@personal.com" -f "jalaj-personal-github"
</code></pre>
<p>After running these commands, navigate to the SSH directory by using the command:</p>
<pre><code class="lang-plaintext">cd ~/.ssh
</code></pre>
<p>Run the ls command to verify that you have four files:</p>
<pre><code class="lang-plaintext">jalaj-work-github
jalaj-work-github.pub
jalaj-personal-github
jalaj-personal-github.pub
</code></pre>
<p>One is a public file, and the other is private. Now, copy the content of the .pub file and add it to the SSH keys section of the respective GitHub account.</p>
<p>Here's how to copy the content to your clipboard:</p>
<pre><code class="lang-plaintext">pbcopy &lt; jalaj-work-github.pub
</code></pre>
<p>Now, go to GitHub and set up the SSH key. You can do that by following the steps mentioned in the <a target="_blank" href="https://docs.github.com/en/github-ae@latest/authentication/connecting-to-github-with-ssh/adding-a-new-ssh-key-to-your-github-account#adding-a-new-ssh-key-to-your-account">github docs to add SSH key to your account</a></p>
<p>After completing this step, add the private keys to the Apple Keychain. Assuming you are still in the ~/.ssh directory, use the following command:</p>
<pre><code class="lang-plaintext">ssh-add --apple-use-keychain jalaj-work-github
</code></pre>
<p>Replace "jalaj-work-github" with the actual file path.</p>
<p>Do the same for the other account.</p>
<h2 id="heading-configuring-ssh">Configuring SSH</h2>
<p>Now, create a configuration file for SSH to know which configuration to use based on the origin.</p>
<p>In the ~/.ssh directory, create a file named config if it doesn't already exist:</p>
<pre><code class="lang-plaintext">touch config &amp;&amp; code config # or vi config
</code></pre>
<p>Add the following content to the config file:</p>
<pre><code class="lang-plaintext">Host github.com-work
  HostName github.com
  User git
  IdentityFile ~/.ssh/jalaj-work-github

Host github.com-personal
  HostName github.com
  User git
  IdentityFile ~/.ssh/jalaj-personal-github
</code></pre>
<p>After adding this, whenever you pull a repo or create a new repo, you need to update the remote URL for the repo to match the host value in the config.</p>
<p>Here's a one-liner to do just that:</p>
<pre><code class="lang-plaintext">git remote set-url origin git@github.com-personal:username/reponame.git
</code></pre>
<h3 id="heading-breaking-it-down">Breaking it down:</h3>
<ul>
<li><p>"<a target="_blank" href="mailto:git@github.com">git@github.com</a>-personal" is the host added in the config file.</p>
</li>
<li><p>"username" is your GitHub username.</p>
</li>
<li><p>"reponame" is your GitHub repository name.</p>
</li>
</ul>
<h2 id="heading-setting-up-user-information">Setting Up User Information</h2>
<p>To let Git know your email and username to attach to the commits, you have two options: manual setup for each repo or using Git config files.</p>
<p>For the manual setup for each repo, run these commands:</p>
<pre><code class="lang-plaintext">git config --local user.email "jalaj@personal.com"
git config --local user.name "jalaj"
</code></pre>
<p>You need to remember to do this for every repo on your system.</p>
<p>To make this easier, you can set a global config for work/personal GitHub accounts and manually configure the other one:</p>
<pre><code class="lang-plaintext">git config --global user.email "jalaj@work.com"
git config --global user.name "jalaj"
</code></pre>
<p>For an even easier approach, you can use Git config files.</p>
<p>First, open or create a <code>~/.gitconfig</code> file in the root directory, and create two other files: <code>~/.gitconfig-work</code> and <code>~/.gitconfig-personal</code>:</p>
<pre><code class="lang-plaintext">touch ~/.gitconfig ~/.gitconfig-work ~/.gitconfig-personal
</code></pre>
<p>Open <code>~/.gitconfig-work</code> in your favorite editor and add the following values:</p>
<pre><code class="lang-plaintext">[user]
 name = jalaj
 email = jalaj@work.com
</code></pre>
<p>Do the same for the <code>~/.gitconfig-personal</code> file.</p>
<p>Now, open <code>~/.gitconfig</code> and add the following condition:</p>
<pre><code class="lang-plaintext">[includeIf "gitdir:~/projects/personal/"]
  path = ~/.gitconfig-personal
[includeIf "gitdir:~/projects/work/"]
  path = ~/.gitconfig-work
</code></pre>
<p>This configuration loads the correct config file based on the directory where your project resides. Feel free to change the directory to match your work and personal projects.</p>
<p>With all these steps, you can now use both personal and work GitHub accounts on a single system, and everything will work automatically due to the one-time setup you've completed.</p>
<h2 id="heading-summary">Summary</h2>
<h3 id="heading-ssh-key-setup">SSH Key Setup</h3>
<ul>
<li><p>Generate two SSH keys: one for work and one for personal accounts.</p>
<ul>
<li><p><code>ssh-keygen -t rsa -C "</code><a target="_blank" href="mailto:jalaj@work.com"><code>jalaj@work.com</code></a><code>" -f "jalaj-work-github"</code></p>
</li>
<li><p><code>ssh-keygen -t rsa -C "</code><a target="_blank" href="mailto:jalaj@personal.com"><code>jalaj@personal.com</code></a><code>" -f "jalaj-personal-github"</code></p>
</li>
</ul>
</li>
<li><p>Copy the content of the .pub files to your GitHub accounts.</p>
</li>
</ul>
<h3 id="heading-keychain-configuration">Keychain Configuration</h3>
<ul>
<li><p>Add the private keys to Apple Keychain.</p>
<ul>
<li><p><code>ssh-add --apple-use-keychain jalaj-work-github</code></p>
</li>
<li><p>Repeat for the other account.</p>
</li>
</ul>
</li>
</ul>
<h3 id="heading-ssh-configuration">SSH Configuration</h3>
<ul>
<li><p>Create a <code>config</code> file in <code>~/.ssh</code> with host-specific settings.</p>
<ul>
<li><p>Define Hosts for both work and personal GitHub accounts.</p>
</li>
<li><p>Specify the <code>IdentityFile</code> for each.</p>
</li>
</ul>
</li>
</ul>
<h3 id="heading-update-git-remotes">Update Git Remotes</h3>
<ul>
<li><p>For existing repositories, update the remote URL to match the host value in the config file.</p>
<ul>
<li><code>git remote set-url origin</code> <a target="_blank" href="mailto:git@github.com"><code>git@github.com</code></a><code>-personal:username/reponame.git</code></li>
</ul>
</li>
</ul>
<h3 id="heading-user-information">User Information</h3>
<ul>
<li><p>Set your email and username for commits.</p>
<ul>
<li><p>For each repository, use:</p>
<ul>
<li><p><code>git config --local</code> <a target="_blank" href="http://user.email"><code>user.email</code></a> <code>"</code><a target="_blank" href="mailto:jalaj@personal.com"><code>jalaj@personal.com</code></a><code>"</code></p>
</li>
<li><p><code>git config --local</code> <a target="_blank" href="http://user.name"><code>user.name</code></a> <code>"jalaj"</code></p>
</li>
</ul>
</li>
<li><p>Or set a global config:</p>
<ul>
<li><p><code>git config --global</code> <a target="_blank" href="http://user.email"><code>user.email</code></a> <code>"</code><a target="_blank" href="mailto:jalaj@work.com"><code>jalaj@work.com</code></a><code>"</code></p>
</li>
<li><p><code>git config --global</code> <a target="_blank" href="http://user.name"><code>user.name</code></a> <code>"jalaj"</code></p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="heading-advanced-git-config-optional">Advanced Git Config (Optional)</h3>
<ul>
<li>Create and configure <code>~/.gitconfig</code>, <code>~/.gitconfig-work</code>, and <code>~/.gitconfig-personal</code> files to manage user information for different projects based on their directory.</li>
</ul>
<p>With these steps, you can effectively manage both your personal and work GitHub accounts on your Mac. If you have any questions or doubts, feel free to reach out, and you can also contact me on Twitter. Thanks for reading!</p>
]]></content:encoded></item><item><title><![CDATA[Keeping the Footer at the Bottom of the Page]]></title><description><![CDATA[When creating a website with dyanmic content, we sometimes face a problem where the content on the page doesn't fill the page and the footer, rather than staying in the bottom, moves up leaving blank space beneath it.
There are many ways to solve thi...]]></description><link>https://blog.jalaj.dev/keeping-the-footer-at-the-bottom-of-the-page</link><guid isPermaLink="true">https://blog.jalaj.dev/keeping-the-footer-at-the-bottom-of-the-page</guid><category><![CDATA[CSS]]></category><category><![CDATA[CSS Grid]]></category><category><![CDATA[flexbox]]></category><category><![CDATA[HTML5]]></category><category><![CDATA[HTML]]></category><dc:creator><![CDATA[Jalaj Gupta]]></dc:creator><pubDate>Thu, 21 Jul 2022 17:20:59 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1622971299349/r9l2SLyPE.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When creating a website with dyanmic content, we sometimes face a problem where the content on the page doesn't fill the page and the footer, rather than staying in the bottom, moves up leaving blank space beneath it.</p>
<p>There are many ways to solve this problem like,</p>
<ul>
<li><p><strong>Absolute positioning the footer at the bottom</strong> (<em>this  doesn't work when content grows larger than the viewport, the footer gets ‘stuck’ to the bottom of the viewport.</em>)</p>
</li>
<li><p><strong>Using sticky or fixed positioning</strong> (<em>this works if you want your footer to always be visible ;(</em> </p>
</li>
<li><p><strong>Using Flexbox or Grid</strong> (<em>Both of these technologies allows us to get the behaviour we want but in my opinion using grid is easier and more elegant</em>)</p>
</li>
</ul>
<p>I'll explain how to use grid to push footer at the bottom in this post and let's leave flexbox for a later post.</p>
<h2 id="heading-page-structure">Page Structure</h2>
<p>Your page structure should match the html below.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
   <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"page-wrapper"</span>&gt;</span>
       <span class="hljs-tag">&lt;<span class="hljs-name">header</span>&gt;</span>HEADER<span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span>
       <span class="hljs-tag">&lt;<span class="hljs-name">main</span>&gt;</span>MAIN<span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span>
       <span class="hljs-tag">&lt;<span class="hljs-name">footer</span>&gt;</span>FOOTER<span class="hljs-tag">&lt;/<span class="hljs-name">footer</span>&gt;</span>
   <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
</code></pre>
<p>Maybe your page structure is somewhat more complex, in that case you need to keep in mind to put your Header, Footer and Main inside a parent container.</p>
<h2 id="heading-applying-css-magic">Applying CSS Magic</h2>
<pre><code class="lang-css"><span class="hljs-selector-class">.page-wrapper</span> {
    <span class="hljs-attribute">min-height</span>: <span class="hljs-number">100vh</span>;
    <span class="hljs-attribute">display</span>: grid;
    <span class="hljs-attribute">grid-template-columns</span>: <span class="hljs-number">100%</span>;
    <span class="hljs-attribute">grid-template-rows</span>: auto <span class="hljs-number">1</span>fr auto;
}
</code></pre>
<p>Let see what each line of above code does.</p>
<ul>
<li><code>min-height: 100vh</code> -  Here we are telling the browser to fill the entire viewport</li>
<li><code>display: grid</code> - This line makes the element a grid container</li>
<li><code>grid-template-columns: 1fr</code> - We are creating a single column and giving it a width of <code>1fr</code>. If that <code>1fr</code> is new to you, it essentially means “take the remaining space” which, in this case, is the entire width of the grid container, <code>.page-wrapper</code>. <br /></li>
</ul>
<p>Now the most important line, </p>
<ul>
<li><p><strong><code>grid-template-rows: auto 1fr auto</code></strong>:  </p>
<ul>
<li>Here we are telling the brower to create three rows. The first and third row, which is the header and footer, respectively, are sized with <code>auto</code>, which means they’ll take up as much space as needed. In other words: no need for hard-coded sizes! (isn't css grid just awesome?)</li>
</ul>
<ul>
<li>The middle row is our content. We’ve assigned it a size of <code>1fr</code> which, again, just means it takes up all of the remaining space that’s left over from the other two rows. If you’re wondering why we aren’t making it <code>auto</code> as well, it’s because the entire grid spans the viewport’s whole height, so we need one section to grow and fill up any unused space. </li>
</ul>
</li>
</ul>
<p>I've prepared a working example on codepen. Go ahead and see what happens when you type more text.</p>
<iframe height="506" style="width:100%" src="https://codepen.io/jalajcodes/embed/Rwpyeqq?height=506&amp;theme-id=dark&amp;default-tab=result">
  See the Pen <a href="https://codepen.io/jalajcodes/pen/Rwpyeqq">Keeping the Footer where it belongs</a> by Jalaj
  (<a href="https://codepen.io/jalajcodes">@jalajcodes</a>) on <a href="https://codepen.io">CodePen</a>.
</iframe>


]]></content:encoded></item><item><title><![CDATA[Creating Page Transitions in React using Framer Motion]]></title><description><![CDATA[Page transitions are animated transition between pages which are used to give websites that extra touch that distinguishes them as a top-notch and worthy of a good browse, when applied correctly they not only give a sense of liveliness but also helps...]]></description><link>https://blog.jalaj.dev/creating-page-transitions-in-react-using-framer-motion</link><guid isPermaLink="true">https://blog.jalaj.dev/creating-page-transitions-in-react-using-framer-motion</guid><category><![CDATA[React]]></category><category><![CDATA[animation]]></category><category><![CDATA[Frontend Development]]></category><dc:creator><![CDATA[Jalaj Gupta]]></dc:creator><pubDate>Thu, 14 Jul 2022 17:21:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1620817294069/8mjZL-nk-.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Page transitions are animated transition between pages which are used to give websites that extra touch that distinguishes them as a top-notch and worthy of a good browse, when applied correctly they not only give a sense of liveliness but also helps greatly with navigation.</p>
<p>We all love to see smooth and cool page transitions when browsing websites. I've always been fascinated by them and wanted to learn how to add those to my sites. Libraries like Barba makes it very easy to add them in our site, but they don't work well with React.</p>
<p>Thankfully, Framer Motion makes it possible (and easier) to create sleek and smooth page transitions in React and similar libraries and in this article, I'll show you how. Scroll down to the bottom if you want to see the final demo.</p>
<h2 id="heading-framer-motion">Framer Motion</h2>
<p>The basics of Framer Motion involve creating a <code>motion</code> component and passing some values to set the styles we want an element to start at and animate to. So if I want to have an element to come in from the left when it's rendered, I can play with the <code>x</code> property. Check out the sandbox below, try changing <code>x</code> to <code>y</code> and see what happens.</p>
<pre><code class="lang-jsx">&lt;motion.div
    initial={{ <span class="hljs-attr">opacity</span>: <span class="hljs-number">0</span>, <span class="hljs-attr">x</span>: <span class="hljs-string">"-100vh"</span> }}
    animate={{ <span class="hljs-attr">opacity</span>: <span class="hljs-number">1</span>, <span class="hljs-attr">x</span>: <span class="hljs-number">0</span> }}
&gt;
   <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Animated text<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span></span>
 &lt;/motion.div&gt;
</code></pre>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codesandbox.io/embed/lingering-sea-67ldq?fontsize=14&amp;theme=dark">https://codesandbox.io/embed/lingering-sea-67ldq?fontsize=14&amp;theme=dark</a></div>
<h2 id="heading-animatepresence">AnimatePresence</h2>
<p>In the above example, you should notice that the top text slides in as it's being mounted, while the normal text renders without any sort of transition. The slide in is smoother, but both texts exhibit the same behavior when they are unmounted. Even though our <code>motion.div</code> component slides in, it doesn't slide out. </p>
<p>To get the desired behavior, we can import and wrap <code>AnimatePresence</code> around our conditionally rendered element. Now we can use the exit prop on our <code>motion.div</code> to have the element slide out as it's being unmounted.</p>
<pre><code class="lang-jsx">&lt;AnimatePresence&gt;
          {isDisplayed &amp;&amp; (
            <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">motion.div</span>
                <span class="hljs-attr">initial</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">x:</span> "<span class="hljs-attr">-100vh</span>" }}
                <span class="hljs-attr">animate</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">x:</span> <span class="hljs-attr">0</span> }}
                <span class="hljs-attr">exit</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">x:</span> "<span class="hljs-attr">100vh</span>" }}
              &gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Animated text<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">motion.div</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Normal text<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
            <span class="hljs-tag">&lt;/&gt;</span></span>
          )}
&lt;/AnimatePresence&gt;
</code></pre>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codesandbox.io/embed/small-butterfly-vb9vc?fontsize=14&amp;theme=dark">https://codesandbox.io/embed/small-butterfly-vb9vc?fontsize=14&amp;theme=dark</a></div>
<p>Pretty cool, huh? Let's reuse the above logic to apply animation when a route is changed in this simple react application.</p>
<h3 id="heading-starter-sandbox">Starter Sandbox</h3>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codesandbox.io/embed/page-transition-starter-q17gq?fontsize=14&amp;theme=dark&amp;view=preview">https://codesandbox.io/embed/page-transition-starter-q17gq?fontsize=14&amp;theme=dark&amp;view=preview</a></div>
<p>Our first step is to wrap our pages inside a <code>&lt;AnimatePresence&gt;</code>. Where we wrap it will depend on where our router is rendering the pages. In our case, we'll do that in <code>App.js</code> file because that's where routes are defined. </p>
<p>AnimatePresence demands that each of its direct children needs to have a unique key prop so it can track their presence in the tree. We can use the <code>location</code> object from the <code>useLocation</code> hook by importing it from <code>react-router-dom</code>.</p>
<p>Here's how the <code>App.js</code> file will look like after making the necessary changes. Notice that I've kept the <code>&lt;Navbar/&gt;</code> component outside of <code>AnimatePresence</code> because we don't want it to animate with the whole page.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { Route, Switch, useLocation } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-router-dom"</span>;
<span class="hljs-keyword">import</span> { AnimatePresence } <span class="hljs-keyword">from</span> <span class="hljs-string">"framer-motion"</span>;

<span class="hljs-keyword">import</span> Home <span class="hljs-keyword">from</span> <span class="hljs-string">"./pages/Home.js"</span>;
<span class="hljs-keyword">import</span> About <span class="hljs-keyword">from</span> <span class="hljs-string">"./pages/About.js"</span>;
<span class="hljs-keyword">import</span> Contact <span class="hljs-keyword">from</span> <span class="hljs-string">"./pages/Contact.js"</span>;
<span class="hljs-keyword">import</span> Navbar <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/Navbar.js"</span>;

<span class="hljs-keyword">const</span> App = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> location = useLocation();

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Navbar</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">AnimatePresence</span> <span class="hljs-attr">exitBeforeEnter</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Switch</span> <span class="hljs-attr">location</span>=<span class="hljs-string">{location}</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{location.pathname}</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">exact</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/"</span> <span class="hljs-attr">component</span>=<span class="hljs-string">{Home}</span> /&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/about"</span> <span class="hljs-attr">component</span>=<span class="hljs-string">{About}</span> /&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/contact"</span> <span class="hljs-attr">component</span>=<span class="hljs-string">{Contact}</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">Switch</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">AnimatePresence</span>&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>If you look closely you will notice that I've added a prop called <code>exitBeforeEnter</code> to AnimatePresence. This prop guarantees that our component will have unmounted (i.e. completed its exit animation) before allowing the new component to animate in. If we remove this prop the exit and enter animation will take place at the same time and we won't be able to see the enter animation while changing routes.</p>
<p>Now all that's left is to use a <code>motion</code> component in our pages and define our <code>initial</code>, <code>animate</code>, and <code>exit</code> props like we did earlier!</p>
<p>So, we'll wrap the return value of all the three pages of our site with a <code>motion.div</code> component and add the necessary props. We'll also add a <code>transition</code> prop to change the duration of the animation so that we can see it more easily. Here's how <code>Home.js</code> will look like after making the changes.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> Banner <span class="hljs-keyword">from</span> <span class="hljs-string">"../components/Banner.js"</span>;
<span class="hljs-keyword">import</span> { motion } <span class="hljs-keyword">from</span> <span class="hljs-string">"framer-motion"</span>;

<span class="hljs-keyword">const</span> Home = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">motion.div</span>
      <span class="hljs-attr">initial</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">opacity:</span> <span class="hljs-attr">0</span>, <span class="hljs-attr">x:</span> <span class="hljs-attr">-200</span> }}
      <span class="hljs-attr">animate</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">opacity:</span> <span class="hljs-attr">1</span>, <span class="hljs-attr">x:</span> <span class="hljs-attr">0</span> }}
      <span class="hljs-attr">exit</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">opacity:</span> <span class="hljs-attr">0</span>, <span class="hljs-attr">x:</span> <span class="hljs-attr">200</span> }}
      <span class="hljs-attr">transition</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">duration:</span> <span class="hljs-attr">0.7</span> }}
    &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Banner</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"Welcome stranger!"</span> <span class="hljs-attr">subtitle</span>=<span class="hljs-string">"Put something witty here!"</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"container"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Welcome<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
          Lorem ipsum........
        <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
          Maecenas dapibus.......
        <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">motion.div</span>&gt;</span></span>
  );
};
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Home;
</code></pre>
<h3 id="heading-final-demo">Final Demo</h3>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codesandbox.io/embed/page-transition-finished-owjid?fontsize=14&amp;theme=dark">https://codesandbox.io/embed/page-transition-finished-owjid?fontsize=14&amp;theme=dark</a></div>
<h2 id="heading-conclusion">Conclusion</h2>
<p>I hope you enjoyed reading the article and learned something from it.</p>
<p>We have only scratched the surface of what framer motion can do. In the next post, we'll use some advanced features of framer motion like <code>AnimateSharedLayout</code> to create a fairly complex page transition.</p>
]]></content:encoded></item><item><title><![CDATA[Reversing a String in Javascript]]></title><description><![CDATA[In this post I will explain different methods to reverse the string using javascript, so without any further ado let get right into it.
Below given code is how I wrote the solution at first, I'll explain it and after that discuss other methods of sol...]]></description><link>https://blog.jalaj.dev/reversing-a-string-in-javascript</link><guid isPermaLink="true">https://blog.jalaj.dev/reversing-a-string-in-javascript</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[algorithms]]></category><category><![CDATA[interview]]></category><dc:creator><![CDATA[Jalaj Gupta]]></dc:creator><pubDate>Thu, 16 Jun 2022 17:25:13 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1621764485915/RqlzDtWVj.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this post I will explain different methods to reverse the string using javascript, so without any further ado let get right into it.</p>
<p>Below given code is how I wrote the solution at first, I'll explain it and after that discuss other methods of solving the problem.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">reverseString</span>(<span class="hljs-params">str</span>) </span>{
    <span class="hljs-keyword">let</span> arr = str.split(<span class="hljs-string">""</span>);
    arr.reverse();
    str = arr.join(<span class="hljs-string">""</span>);
    <span class="hljs-keyword">return</span> str;
}
</code></pre>
<p>What above code does is it first uses the <code>split</code> function which splits the passed string object into array of strings by separating strings into substrings.</p>
<p>After that I reversed the array we created above using <code>reverse</code> method. In the last I joined all the elements of array into string using the <code>join</code> method and returned the string.</p>
<p>So that's my solution to the challenge. Now let’s talk about other ways.</p>
<p>To make above code shorter we can chain methods and return immediately.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">reverseString</span>(<span class="hljs-params">str</span>)</span>{
    <span class="hljs-keyword">return</span> str.split(<span class="hljs-string">""</span>).reverse().join(<span class="hljs-string">""</span>);
}
</code></pre>
<p>While we're at it we can make it even shorter if we use ES6 arrow function syntax.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> reverseString = <span class="hljs-function"><span class="hljs-params">str</span> =&gt;</span> str.split(<span class="hljs-string">""</span>).reverse().join(<span class="hljs-string">""</span>);
</code></pre>
<p>Now imagine your interviewer saying, 'Great, now solve it without using these inbuilt methods with a simple for loop.'</p>
<p>Don't sweat yet! Below I'll tell how to do just that...</p>
<h2 id="heading-reversing-a-string-using-for-loop">Reversing a string using for loop</h2>
<p>We can also reverse a String without using builtin methods like reverse by using for loop.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">reverseString</span>(<span class="hljs-params">str</span>) </span>{
    <span class="hljs-keyword">let</span> newString = <span class="hljs-string">""</span>;
    <span class="hljs-keyword">for</span> (i = str.length - <span class="hljs-number">1</span>; i &gt;= <span class="hljs-number">0</span>; i--) {
        newString += str[i];
    }
    <span class="hljs-keyword">return</span> newString;
}
</code></pre>
<p>Okay so what I am doing in this code is at first I declared a new variable to hold our reversed string.</p>
<p>After that I created a for loop. The starting point of the for loop is <code>str.length - 1</code>. which is the index of the last character of the string. As long as the value of <code>i</code> is greater than or equal to zero the loop will go on and we decrement the value of i with each iteration. </p>
<p>Checkout the pseudocode explaining the process below.</p>
<p>For example let’s suppose the string passed to the function is “hello”.</p>
<pre><code>Now hello’s length equals <span class="hljs-number">5</span>
For each iteration: i <span class="hljs-operator">=</span> str.<span class="hljs-built_in">length</span> – <span class="hljs-number">1</span> and newString <span class="hljs-operator">=</span> newString <span class="hljs-operator">+</span> str[i]
First iteration: i <span class="hljs-operator">=</span> <span class="hljs-number">5</span> – <span class="hljs-number">1</span> <span class="hljs-operator">=</span> <span class="hljs-number">4</span>, newString <span class="hljs-operator">=</span> “” <span class="hljs-operator">+</span> “o” <span class="hljs-operator">=</span> “o”
Second iteration: i <span class="hljs-operator">=</span> <span class="hljs-number">4</span> – <span class="hljs-number">1</span> <span class="hljs-operator">=</span> <span class="hljs-number">3</span>, newString <span class="hljs-operator">=</span> “o” <span class="hljs-operator">+</span> “l” <span class="hljs-operator">=</span> “ol”
Third iteration: i <span class="hljs-operator">=</span> <span class="hljs-number">3</span> – <span class="hljs-number">1</span> <span class="hljs-operator">=</span> <span class="hljs-number">2</span>, newString <span class="hljs-operator">=</span> “ol” <span class="hljs-operator">+</span> “l” <span class="hljs-operator">=</span> “oll”
Fourth iteration: i <span class="hljs-operator">=</span> <span class="hljs-number">2</span> – <span class="hljs-number">1</span> <span class="hljs-operator">=</span> <span class="hljs-number">1</span>, newString <span class="hljs-operator">=</span> “oll” <span class="hljs-operator">+</span> “e” <span class="hljs-operator">=</span> “olle”
Fifth iteration: i <span class="hljs-operator">=</span> <span class="hljs-number">1</span> – <span class="hljs-number">1</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>, newString <span class="hljs-operator">=</span> “olle” <span class="hljs-operator">+</span> “h” <span class="hljs-operator">=</span> “olleh”
End of the FOR Loop
</code></pre><p>Aaaand in the last we returned the reversed string.</p>
<p>Hope you got that! If not then don’t hesitate to comment and I’ll be more than happy to help.</p>
<p>Still awake? Let's see yet another method to reverse your strings.</p>
<h2 id="heading-reversing-string-using-recursion">Reversing string using Recursion</h2>
<p>In this method, I will use two methods: the <code>String.prototype.substr()</code> method and the <code>String.prototype.charAt()</code> method.</p>
<p>The <code>substr()</code> method returns the characters in a string beginning at the specified location through the specified number of characters.</p>
<pre><code class="lang-js"><span class="hljs-string">"hello"</span>.substr(<span class="hljs-number">1</span>); <span class="hljs-comment">// "ello"</span>
</code></pre>
<p>The <code>charAt()</code> method returns the specified character from a string.</p>
<pre><code class="lang-js"><span class="hljs-string">"hello"</span>.charAt(<span class="hljs-number">0</span>); <span class="hljs-comment">// "h"</span>
</code></pre>
<p>Now the whole code,</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">reverseString</span>(<span class="hljs-params">str</span>) </span>{
    <span class="hljs-keyword">if</span> (str === <span class="hljs-string">""</span>) { <span class="hljs-comment">// This is the terminal case that will end the recursion</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">""</span>;
    } <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">return</span> reverseString(str.substr(<span class="hljs-number">1</span>)) + str.charAt(<span class="hljs-number">0</span>);
    }
}
reverseString(<span class="hljs-string">"hello"</span>); <span class="hljs-comment">// olleh</span>
</code></pre>
<p>Explanation: </p>
<pre><code class="lang-js">First Part <span class="hljs-keyword">of</span> the recursion method

  We need to remember that there won’t be just one call, we’ll have several nested calls

  Each call will <span class="hljs-keyword">return</span>       reverseString(str.subst(<span class="hljs-number">1</span>))   +    str.charAt(<span class="hljs-number">0</span>)

  <span class="hljs-number">1</span>st call – reverseString(<span class="hljs-string">"Hello"</span>)   will <span class="hljs-keyword">return</span>   reverseString(<span class="hljs-string">"ello"</span>)    + <span class="hljs-string">"h"</span>
  <span class="hljs-number">2n</span>d call – reverseString(<span class="hljs-string">"ello"</span>)    will <span class="hljs-keyword">return</span>   reverseString(<span class="hljs-string">"llo"</span>)      + <span class="hljs-string">"e"</span>
  <span class="hljs-number">3</span>rd call – reverseString(<span class="hljs-string">"llo"</span>)     will <span class="hljs-keyword">return</span>   reverseString(<span class="hljs-string">"lo"</span>)       + <span class="hljs-string">"l"</span>
  <span class="hljs-number">4</span>th call – reverseString(<span class="hljs-string">"lo"</span>)      will <span class="hljs-keyword">return</span>   reverseString(<span class="hljs-string">"o"</span>)        + <span class="hljs-string">"l"</span>
  <span class="hljs-number">5</span>th call – reverseString(<span class="hljs-string">"o"</span>)       will <span class="hljs-keyword">return</span>   reverseString(<span class="hljs-string">""</span>)          + <span class="hljs-string">"o"</span>

Second part <span class="hljs-keyword">of</span> the recursion method

  The method hits the <span class="hljs-keyword">if</span> condition and the most highly nested call returns immediately

  <span class="hljs-number">5</span>th call will <span class="hljs-keyword">return</span> reverseString(<span class="hljs-string">""</span>) + <span class="hljs-string">"o"</span> = <span class="hljs-string">"o"</span>
  <span class="hljs-number">4</span>th call will <span class="hljs-keyword">return</span> reverseString(<span class="hljs-string">"o"</span>) + <span class="hljs-string">"l"</span> = <span class="hljs-string">"o"</span> + <span class="hljs-string">"l"</span>
  <span class="hljs-number">3</span>rd call will <span class="hljs-keyword">return</span> reverseString(<span class="hljs-string">"lo"</span>) + <span class="hljs-string">"l"</span> = <span class="hljs-string">"o"</span> + <span class="hljs-string">"l"</span> + <span class="hljs-string">"l"</span>
  <span class="hljs-number">2n</span>d call will <span class="hljs-keyword">return</span> reverserString(<span class="hljs-string">"llo"</span>) + <span class="hljs-string">"e"</span> = <span class="hljs-string">"o"</span> + <span class="hljs-string">"l"</span> + <span class="hljs-string">"l"</span> + <span class="hljs-string">"e"</span>
  <span class="hljs-number">1</span>st call will <span class="hljs-keyword">return</span> reverserString(<span class="hljs-string">"ello"</span>) + <span class="hljs-string">"h"</span> = <span class="hljs-string">"o"</span> + <span class="hljs-string">"l"</span> + <span class="hljs-string">"l"</span> + <span class="hljs-string">"e"</span> + <span class="hljs-string">"h"</span>
</code></pre>
<p>In the end we get the reversed string.</p>
<p>So this one was heavy and I doubt if someone could come up with this solution on his own in interview. If you don't understand this solution right now its totally fine.</p>
<p>Now continuing on our tradition from above, here's the shortened version of above code using arrow functions and ternary operators.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> reversedString = <span class="hljs-function"><span class="hljs-params">str</span> =&gt;</span> (str === <span class="hljs-string">""</span>) ? <span class="hljs-string">""</span> : reverseString(str.substr(<span class="hljs-number">1</span>)) + str.charAt(<span class="hljs-number">0</span>);
</code></pre>
<h3 id="heading-parting-words">Parting words</h3>
<p>Hope you learned something from the article. Reversing a string is a simple problem which can be useful in solving more complex problems in interview or maybe if you are lucky enough you'll be asked just to reverse a string ;)</p>
<p>See you in next post, till then Happy Coding!</p>
]]></content:encoded></item><item><title><![CDATA[The easy-peasy React state management library]]></title><description><![CDATA[Every application needs to manage state. In React, we can go a long way using hooks, and in particular useState(), and passing props around.
In my personal projects when things get more complicated than that, I like to immediately jump to a state man...]]></description><link>https://blog.jalaj.dev/the-easy-peasy-react-state-management-library</link><guid isPermaLink="true">https://blog.jalaj.dev/the-easy-peasy-react-state-management-library</guid><category><![CDATA[React]]></category><category><![CDATA[Redux]]></category><dc:creator><![CDATA[Jalaj Gupta]]></dc:creator><pubDate>Sun, 23 May 2021 13:00:50 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1621774817073/jV7BSYKiE.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Every application needs to manage state. In React, we can go a long way using hooks, and in particular <code>useState()</code>, and passing props around.</p>
<p>In my personal projects when things get more complicated than that, I like to immediately jump to a state management library. One of my favorites lately is <code>easy-peasy</code>.</p>
<p>It builds on top of Redux, and it provides a simpler way to interact with state.</p>
<p>I like to keep my code as simple as possible. Simple is understandable. Simple is beautiful.</p>
<p>Complexity should be avoided at all costs, and if possible hidden away in libraries that expose a simple interface to us. It’s the case of this library, and that’s why I like it!</p>
<p>Install it using:</p>
<pre><code class="lang-sh">yarn add easy-peasy
</code></pre>
<p>First of all we need to create a <strong>store</strong>. The store is the place where we’ll store our state, and the functions needed to modify it.</p>
<p>Create the store in the file <code>store.js</code> in the root of your project, with this content:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { createStore, action } <span class="hljs-keyword">from</span> <span class="hljs-string">'easy-peasy'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> createStore({})
</code></pre>
<p>We’ll add more things to this file later.</p>
<p>Now let's wrap our React app into the <code>StoreProvider</code> component provided by <code>easy-peasy</code>. It depends on what you use. With <code>create-react-app</code> for example, I'll add this to my <code>index.js</code> file:</p>
<pre><code class="lang-js"><span class="hljs-comment">//...</span>
<span class="hljs-keyword">import</span> { StoreProvider } <span class="hljs-keyword">from</span> <span class="hljs-string">'easy-peasy'</span>
<span class="hljs-keyword">import</span> store <span class="hljs-keyword">from</span> <span class="hljs-string">'../store'</span>

ReactDOM.render(
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">React.StrictMode</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">StoreProvider</span> <span class="hljs-attr">store</span>=<span class="hljs-string">{store}</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">App</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">StoreProvider</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">React.StrictMode</span>&gt;</span></span>,
  <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'root'</span>)
)
</code></pre>
<p>This operation makes now our store available in every component of the app.</p>
<p>Now we're ready to go in the <code>store.js</code> file and add some state, and some actions to change that state.</p>
<p>Let’s do a simple example. We can create a <code>name</code> state, and we create a <code>setName</code> action to change the name:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { createStore, action } <span class="hljs-keyword">from</span> <span class="hljs-string">'easy-peasy'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> createStore({
  <span class="hljs-attr">name</span>: <span class="hljs-string">''</span>,
  <span class="hljs-attr">setName</span>: action(<span class="hljs-function">(<span class="hljs-params">state, payload</span>) =&gt;</span> {
    name = payload
  })
})
</code></pre>
<p>Now inside any component of our app we can import <code>useStoreState</code> and <code>useStoreActions</code> from <code>easy-peasy</code>:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { useStoreState, useStoreActions } <span class="hljs-keyword">from</span> <span class="hljs-string">'easy-peasy'</span>
</code></pre>
<p>We use <code>useStoreState</code> to access the store state properties:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> name = useStoreState(<span class="hljs-function">(<span class="hljs-params">state</span>) =&gt;</span> state.name)
</code></pre>
<p>and <code>useStoreActions</code> to access the actions we defined:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> setName = useStoreActions(<span class="hljs-function">(<span class="hljs-params">actions</span>) =&gt;</span> actions.setName)
</code></pre>
<p>Now we can call this action whenever something happens in our app, for example if we click a button:</p>
<pre><code class="lang-jsx">&lt;button
  onClick={<span class="hljs-function">() =&gt;</span> {
    setName(<span class="hljs-string">'newname'</span>)
  }}
&gt;
  <span class="hljs-built_in">Set</span> name
&lt;/button&gt;
</code></pre>
<p>Now any other component that is accessing the state through <code>useStoreState()</code> will see the value updated.</p>
<p>This is a simple example but it all starts from this. We can add as many state variables and as many actions we want, and I found that centralizing it all to a <code>store.js</code> file makes the application very easy to scale.</p>
]]></content:encoded></item><item><title><![CDATA[GiTrack - Statistics and Issue Tracker for Open Source Enthusiasts]]></title><description><![CDATA[👋 Introduction
Hi folks!
First of all, I want to thank the Hashnode team for providing us with the opportunity to get out of our comfort zone (or should I say Tutorial Hell 😉) and actually build and deploy something into the real world. This is my ...]]></description><link>https://blog.jalaj.dev/gitrack-statistics-and-issue-tracker-for-open-source-enthusiasts</link><guid isPermaLink="true">https://blog.jalaj.dev/gitrack-statistics-and-issue-tracker-for-open-source-enthusiasts</guid><category><![CDATA[Amplify Hashnode]]></category><category><![CDATA[hackathon]]></category><category><![CDATA[Next.js]]></category><category><![CDATA[Firebase]]></category><dc:creator><![CDATA[Jalaj Gupta]]></dc:creator><pubDate>Sat, 27 Feb 2021 14:02:50 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1614435522263/VtKRIBVv-.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="introduction">👋 <strong>Introduction</strong></h1>
<p>Hi folks!</p>
<p>First of all, I want to thank the Hashnode team for providing us with the opportunity to get out of our comfort zone (or should I say Tutorial Hell 😉) and actually build and deploy something into the real world. This is my first ever hackathon and I am very excited to share what I created with all of you.</p>
<p>I am Jalaj, a student from India currently pursuing my bachelor's degree. I started contributing to Open source last year in December since then I've contributed to 10+ repositories with over 30+ merged pull requests. </p>
<p>Working in Open Source has taught me a lot of things and I would recommend everyone to try it, we get to learn to work with other people, learn and improve by reading code and a lot of other things which one simply can't get by just watching a tutorial on Youtube or Udemy and creating simple and trivial apps. </p>
<h2 id="introducing-gitrack">✨ <strong>Introducing</strong> <strong>G</strong>i<strong>T</strong>rack</h2>
<ul>
<li><a target="_blank" href="https://dev.d1bycrk5tmifaz.amplifyapp.com/"><strong>Demo</strong></a></li>
<li><a target="_blank" href="https://github.com/jalajcodes/gitrack">Github</a></li>
</ul>
<p>I like to keep track of things and while contributing simultaneously to various repositories, it sometimes gets difficult to keep track of assigned issues and pull requests.  To solve that problem, I built GiTrack, a website for Open Source Enthusiasts to keep track of issues assigned to them and arrange them in a nice looking Kanban board.</p>
<p>I wanted to create something useful but also simple enough for everyone to use, that's why I went with a Kanban board (coz everyone's familiar with it? 😅) besides that I also decide to add a profile page to the website that shows some useful statistics to the user such as the Total number of their contributions, issues they have opened, pull requests they have created, their current streak and a lot more...</p>
<p>Github has their own project board for a per-project basis, but the idea behind GiTrack is to be an issue tracker for all repositories, owned or otherwise, plus you get some nice statistics. </p>
<p>Here's what I had in mind when I started building the website:</p>
<ul>
<li>It should allow users to log in via Github. (who wants to type their email and password?)</li>
<li>It should have a beautiful homepage that lists features of the website.</li>
<li>It should allow users to fetch currently assigned issues directly into the board.</li>
<li>Board state should be saved every time a user makes any changes. So that the next time they visit they can start where they left.</li>
<li>Profile page should list different stats that might be useful for users.</li>
</ul>
<p>I used AWS Amplify to host the site. Amplify's CLI is a dream come true for developers. Just one command and the site is deployed live. </p>
<h2 id="screenshots">💻 Screenshots</h2>
<h3 id="light-mode">Light mode:</h3>
<p><strong>Home page</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1614430330590/eq9F3UEsB.png" alt="Homepage" /></p>
<p><strong>Login Page</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1614430835626/4ycBGDA0Z.png" alt="login screen" /></p>
<p><strong>Statistics</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1614749690295/sf306weEGG.png" alt="ScreenHunter Mar. 03 10.44.png" />
<strong>Issue Tracker</strong>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1614430776186/1gyVU5j8k.png" alt="issue tracker" /></p>
<h3 id="dark-mode">Dark mode:</h3>
<p><strong>Home page</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1614749236268/BG9e34Dpi.png" alt="ScreenHunter Mar. 03 10.37.png" /></p>
<p><strong>Login page</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1614749283639/O5ZQRXxDH.png" alt="ScreenHunter Mar. 03 10.40.png" />
<strong>Statistics</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1614749360358/K4bAOW2bBB.png" alt="ScreenHunter Mar. 03 10.43.png" />
<strong>Issue tracker</strong>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1614749394527/ggt8GF7Qv.png" alt="ScreenHunter Mar. 03 10.48.png" /></p>
<h2 id="features">👀 Features</h2>
<ul>
<li>Issue Tracker</li>
<li>Github Statistics</li>
<li>Fetch issues assigned to you directly from Github.</li>
<li>Client Side Storage using IndexedDB for super fast page loads.</li>
<li>Persistent board state, so no worries about accidentally closing the page.</li>
<li>Beautiful UI</li>
<li>Open Source (checkout code on <a target="_blank" href="https://github.com/jalajcodes">github</a>)</li>
<li>One Click Login using Github OAuth</li>
</ul>
<p>Here's a quick video showing how to use the issue tracker.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/2DoJbP9700Y">https://youtu.be/2DoJbP9700Y</a></div>
<h2 id="build-process">🏢 Build Process</h2>
<p>I really wanted to get out of my comfort zone and build something with the technologies I've never used before. In the tech stack listed below, I was only familiar with Typescript before I started this project.</p>
<h4 id="tech-stack">👩‍💻 Tech Stack</h4>
<ul>
<li>Next JS</li>
<li>Typescript</li>
<li>AWS Amplify</li>
<li>Firebase</li>
<li>Chakra UI</li>
</ul>
<p>I read about this hackathon at the starting of February and I created a repo and pushed the initial commit the on 14th. But after that, I started procrastinating didn't do anything for the next few days. It was 22 when I got some confidence boost and started working on the project again.</p>
<p>I used <a target="_blank" href="https://openchakra.app/">OpenChakra</a> to quickly create a basic boilerplate for the pages and edited it to fit my needs. I chose firebase to do the authentication and storage. I really wanted to use Amplify for auth but they don't have Github OAuth. Anyways, after getting done with a basic MVP of the project I deployed it on Amplify using its CLI. </p>
<p>Here are some of the packages I used: </p>
<ul>
<li><code>nextjs</code> : for building the app</li>
<li><code>typescript</code> for writing <strong>type safe</strong> code</li>
<li><code>react-trello</code> for issue tracker</li>
<li><code>chakra-ui</code> as a UI framework</li>
<li><code>firebase</code> for auth and storage</li>
<li><code>localforage</code> for storing data in indexedDB</li>
<li><code>octokit</code> as a github rest client</li>
</ul>
<h2 id="challenges-faced-while-developing">🏆 Challenges faced while developing</h2>
<ul>
<li><p>Persist board state: I wanted to store board state every time a user makes any change to the tracker board. I could use firebase for this but I wanted to keep it fast. So my only option was to store data in the browser. As the data was complex I was having trouble storing it in localstorage, I did some research and decided to use IndexedDB.</p>
</li>
<li><p>As I wasn't requesting any special permissions while using GitHub OAuth I had to find an alternative way to get assigned issues for the user.  I did some research and used github issues and pull requests search endpoint to get the required data.</p>
</li>
<li><p>Persist login state: Firebase provides a function just for this purpose but it wasn't working as I expected it to be, it logged the user in without checking if there is data in IndexedDB or not so I had to modify it with added logic of fetching the state from indexedDB first and if there is no data in DB then don't log the user in.</p>
</li>
</ul>
<h2 id="whats-next">🌟 What's Next</h2>
<ul>
<li>Add Dark Mode<ul>
<li>Update <strong>3/3/21</strong>: Dark mode added. (Check Screenshots above)</li>
</ul>
</li>
<li>Allow users to get issues they recently commented on.</li>
<li>Allow users to get their merged pull requests. </li>
<li>Keyboard shortcuts</li>
</ul>
<h2 id="conclusion">🥳 Conclusion</h2>
<p>All in all, it was fun working on the project in a strict timeline. Once again thanks to the folks over at Hashnode for this opportunity. </p>
<p>Thanks to the reader for reading this far, I hope you like the app, and please don't hesitate to message me or comment below if you have any feedback.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/jalajcodes/gitrack">https://github.com/jalajcodes/gitrack</a></div>
]]></content:encoded></item></channel></rss>