<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Julien</title>
    <description>The latest articles on DEV Community by Julien (@julien41).</description>
    <link>https://dev.to/julien41</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F686461%2F0ad6510c-8294-4cbe-a35f-c90c8b14f201.jpg</url>
      <title>DEV Community: Julien</title>
      <link>https://dev.to/julien41</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/julien41"/>
    <language>en</language>
    <item>
      <title>Calendar events and emails as a SQL database using Nylas</title>
      <dc:creator>Julien</dc:creator>
      <pubDate>Tue, 27 Aug 2024 13:30:00 +0000</pubDate>
      <link>https://dev.to/julien41/calendar-events-and-emails-as-a-sql-database-using-nylas-3gl</link>
      <guid>https://dev.to/julien41/calendar-events-and-emails-as-a-sql-database-using-nylas-3gl</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/nylas"&gt;Nylas Challenge&lt;/a&gt;: Galaxy Brain.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built and Why
&lt;/h2&gt;

&lt;p&gt;You probably know SQL for relational databases. But what if you could use it to manage your emails and calendar events? That's what I wanted to explore.&lt;br&gt;
It's a bit strange, but actually, it's fun and useful.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/julien040/anyquery" rel="noopener noreferrer"&gt;Anyquery&lt;/a&gt; is a SQL query engine. I created a &lt;a href="https://anyquery.dev/integrations/nylas/" rel="noopener noreferrer"&gt;plugin&lt;/a&gt; for it with Go to make it compatible with Nylas, allowing anyone to manage their emails and events with SQL.&lt;/p&gt;

&lt;p&gt;My main motivation was bulk editing. Let's say I was wrong on the location of a recurrent event. I would need to modify each event location. Using SQL, I can just do&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;nylas_events&lt;/span&gt; 
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="k"&gt;location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Eiffel tower` 
WHERE title = '&lt;/span&gt;&lt;span class="n"&gt;Dentist&lt;/span&gt;&lt;span class="s1"&gt;';
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Moreover, imagine that I have to send bulk emails to my customers. I can just do&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;nylas_emails&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;"to"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'New version 0.3.1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Discover the update'&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;my_customers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or export my 10 next upcoming events to JSON&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;anyquery &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"SELECT * FROM nylas_events WHERE start_at &amp;gt;= date()"&lt;/span&gt; &lt;span class="nt"&gt;--json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SQL is a very powerful language and this plugin opens a new world of possibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/QPYqM7YXcsA"&gt;
&lt;/iframe&gt;
&lt;br&gt;
I have recorded a little demo where I show you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; How to query your calendar events&lt;/li&gt;
&lt;li&gt; How to add an event using an &lt;code&gt;INSERT INTO&lt;/code&gt; statement&lt;/li&gt;
&lt;li&gt; How to update an event using an &lt;code&gt;UPDATE&lt;/code&gt; statement&lt;/li&gt;
&lt;li&gt; How to delete an event using a &lt;code&gt;DELETE&lt;/code&gt; statement&lt;/li&gt;
&lt;li&gt; How to list your unread emails using a &lt;code&gt;SELECT&lt;/code&gt; statement&lt;/li&gt;
&lt;li&gt; How to send an email using an &lt;code&gt;INSERT INTO&lt;/code&gt; statement&lt;/li&gt;
&lt;li&gt; How to trash emails using a &lt;code&gt;DELETE&lt;/code&gt; statement&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm using TablePlus as the SQL IDE, and Anyquery in MySQL mode to run the plugin I made.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;As plugins are required to be in the main repository of anyquery, the plugin I built is available in the &lt;code&gt;plugins/nylas&lt;/code&gt; directory, and  with an MIT License&lt;br&gt;
&lt;a href="https://github.com/julien040/anyquery/tree/main/plugins/nylas" rel="noopener noreferrer"&gt;https://github.com/julien040/anyquery/tree/main/plugins/nylas&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can install it by running &lt;code&gt;anyquery install nylas&lt;/code&gt;. You'll need &lt;a href="https://anyquery.dev/docs/#installation" rel="noopener noreferrer"&gt;anyquery installed&lt;/a&gt;, and a &lt;a href="https://developer.nylas.com/docs/v3/quickstart/#create-a-v3-sandbox-application" rel="noopener noreferrer"&gt;Nylas sandbox account set up&lt;/a&gt; with a grant ID and an API key.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Journey
&lt;/h2&gt;

&lt;p&gt;The Nylas APIs were of great help to query and modify data from different calendar/email providers. I'm particularly proud of the seamless integration with the SQL query engine. &lt;br&gt;
While this project is a submission for the hackathon, I'm pretty sure I'll use it weekly, and others might too. Managing productivity tools with a declarative language is an efficient and effective approach.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>nylaschallenge</category>
      <category>api</category>
      <category>nylas</category>
    </item>
    <item>
      <title>Apple notes is my CMS</title>
      <dc:creator>Julien</dc:creator>
      <pubDate>Mon, 12 Aug 2024 22:00:00 +0000</pubDate>
      <link>https://dev.to/julien41/apple-notes-is-my-cms-4lbl</link>
      <guid>https://dev.to/julien41/apple-notes-is-my-cms-4lbl</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;You may have already come across this meme and the superiority of Apple Notes.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3oghdN4X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://julienc.me/images/articles/apple-notes/meme.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3oghdN4X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://julienc.me/images/articles/apple-notes/meme.png" alt="Meme" width="800" height="642"&gt;&lt;/a&gt;&lt;br&gt;
Well, what if you could use it as a CMS to manage the content of your blog? That’s what I wanted to try for my « Today I learned » website. Here is the end result at &lt;a href="https://til.julienc.me" rel="noopener noreferrer"&gt;https://til.julienc.me&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--v03KT81L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://julienc.me/images/articles/apple-notes/vdso.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--v03KT81L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://julienc.me/images/articles/apple-notes/vdso.png" alt="Website example" width="800" height="553"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Querying Apple Notes
&lt;/h2&gt;

&lt;p&gt;We need a way to fetch the notes from Apple Notes. To do so, we’ll use Anyquery, it’s a SQL database that can query almost anything, including Apple Notes.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install Anyquery at &lt;a href="https://anyquery.dev/docs/#installation" rel="noopener noreferrer"&gt;https://anyquery.dev/docs/#installation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Install the Apple Notes plugin: &lt;code&gt;anyquery install notes&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Query our notes using SQL and save it to JSON (in my case, my notes are in the folder TIL)&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;anyquery &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"SELECT name, html_body, modification_date 
FROM notes_items WHERE folder = 'TIL';"&lt;/span&gt; &lt;span class="nt"&gt;--json&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; notes.json 
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;/ol&gt;

&lt;p&gt;You now have a file &lt;code&gt;notes.json&lt;/code&gt; which contains all your notes in an array of objects. Each object has three properties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The name of the note (&lt;code&gt;name&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Its last modified time (&lt;code&gt;modification_date&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;The body note in HTML (&lt;code&gt;html_body&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Example"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"modification_date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2024-08-11T00:00:00Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"html_body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;h1&amp;gt;Example&amp;lt;/h1&amp;gt;&amp;lt;p&amp;gt;This is an example&amp;lt;/p&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our last task is to connect the website to it&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting the website
&lt;/h2&gt;

&lt;p&gt;Personally, I’m using Astro.JS. Our first task will be to generate the static path for each entry. &lt;br&gt;
To do so, I can just do &lt;code&gt;import notes from "../../notes.json";&lt;/code&gt; and pass it to &lt;code&gt;export function getStaticPaths()&lt;/code&gt;. I’m also using a slugify function to ensure the generated URLs are valid.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// [...blog].astro&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;notes&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../../notes.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;slugify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;+/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[^&lt;/span&gt;&lt;span class="sr"&gt;a-z0-9-&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getStaticPaths&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;notes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;note&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;blog&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;slugify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;note&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;blog&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Astro&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;note&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;notes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;note&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;slugify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;note&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;blog&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once paths are generated, we need to write a little bit of CSS to match the Apple Notes style:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;article&lt;/span&gt;&lt;span class="nc"&gt;.notes&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#454545&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.9rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;font-style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;normal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;400&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;normal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;letter-spacing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-0.015rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;article&lt;/span&gt;&lt;span class="nc"&gt;.notes&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="nd"&gt;:first-child&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#de9807&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="nt"&gt;truncated&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;retrieve&lt;/span&gt; &lt;span class="nt"&gt;the&lt;/span&gt; &lt;span class="nt"&gt;full&lt;/span&gt; &lt;span class="nt"&gt;CSS&lt;/span&gt; &lt;span class="nt"&gt;in&lt;/span&gt; &lt;span class="nt"&gt;the&lt;/span&gt; &lt;span class="nt"&gt;repository&lt;/span&gt; &lt;span class="nt"&gt;at&lt;/span&gt; &lt;span class="nt"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nt"&gt;styles&lt;/span&gt;&lt;span class="nc"&gt;.css&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are now done !&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Congratulations, you're now using Apple Notes as a CMS. It's a powerful, collaborative CMS that is just bound to your iCloud storage limits. You can add images, tables, formatted text, code, etc.&lt;br&gt;
Here is an example of the formatting options:&lt;br&gt;
&lt;a href="https://til.julienc.me/example-of-capabilities" rel="noopener noreferrer"&gt;https://til.julienc.me/example-of-capabilities&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mm1l-kwW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://julienc.me/images/articles/apple-notes/final-result.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mm1l-kwW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://julienc.me/images/articles/apple-notes/final-result.png" alt="Final result" width="800" height="1499"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can deploy your own blog from Apple Notes to Vercel by doing the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clone the repository &lt;code&gt;git clone https://github.com/julien040/apple-notes-cms&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;npm install&lt;/code&gt; or &lt;code&gt;pnpm install&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;chmod u+x deploy.sh&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;vercel&lt;/code&gt; to init and connect the project&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;./deploy.sh&lt;/code&gt; to build and push the project to Vercel&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Links
&lt;/h3&gt;

&lt;p&gt;Source code: &lt;a href="https://github.com/julien040/apple-notes-cms" rel="noopener noreferrer"&gt;https://github.com/julien040/apple-notes-cms&lt;/a&gt;&lt;br&gt;
Result: &lt;a href="https://til.julienc.me/" rel="noopener noreferrer"&gt;https://til.julienc.me/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>database</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Lotir - Share link and images between your phone and your computer</title>
      <dc:creator>Julien</dc:creator>
      <pubDate>Sun, 09 Jan 2022 00:05:03 +0000</pubDate>
      <link>https://dev.to/julien41/lotir-share-link-and-images-between-your-phone-and-your-computer-5d36</link>
      <guid>https://dev.to/julien41/lotir-share-link-and-images-between-your-phone-and-your-computer-5d36</guid>
      <description>&lt;h2&gt;
  
  
  Overview of My Submission
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;I've always found painful to use email to share things to my phone&lt;/strong&gt;. But I've never found any solutions solving this problem efficiently. They would either be too complex or not designed as first for this use case.&lt;br&gt;
So I built this app by myself.&lt;/p&gt;
&lt;h2&gt;
  
  
  Submission Category:
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Action Star&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;This app is event-driven. Each time something is shared from desktop, a notification is sent to phone thanks to Realm Trigger&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Link to Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/julien040" rel="noopener noreferrer"&gt;
        julien040
      &lt;/a&gt; / &lt;a href="https://github.com/julien040/lotir" rel="noopener noreferrer"&gt;
        lotir
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Lotir is an app to share text and images between your devices
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Lotir&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://raw.githubusercontent.com/julien040/lotir/master/assets/Github%20header.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fjulien040%2Flotir%2Fmaster%2Fassets%2FGithub%2520header.png" alt="Header"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What is Lotir ?&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;I've created this app because I couldn't support sending myself an email to share something on my phone.&lt;/p&gt;
&lt;p&gt;Lotir is a simple app that allows you to share images and text between your phone and your computer.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It's free&lt;/li&gt;
&lt;li&gt;No ads&lt;/li&gt;
&lt;li&gt;Simple to use&lt;/li&gt;
&lt;li&gt;Share text and images&lt;/li&gt;
&lt;li&gt;Temporary : Lotir keep your stuff for 7 days&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=48bThHfEfug" title="Lotir - Text sharing demo" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/ce97259d55238c21d62122f51563f97d8841b72d38231263c5d7e66ec8aa3dd9/68747470733a2f2f696d672e796f75747562652e636f6d2f76692f34386254684866456675672f302e6a7067" alt="Demo Image"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;How to use it ?&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;To use the app, you need to install it on your phone. Download it from &lt;a href="https://github.com/julien040/lotir/releases" rel="noopener noreferrer"&gt;Github Release&lt;/a&gt;. To access the computer app, just go to &lt;a href="https://lotir.ml" rel="nofollow noopener noreferrer"&gt;lotir.ml&lt;/a&gt; and set a sync key.&lt;/p&gt;
&lt;p&gt;If you want to know more about this project, it has been built for the MongoDB Atlas hackathon. Check the announcement post &lt;a href="https://dev.to/julien41/lotir-share-link-and-images-between-your-phone-and-your-computer-5d36" rel="nofollow"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Development&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Project structure&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;This repository is monorepo. It contains the following sub-projects:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;mobile&lt;/code&gt; - Mobile apps (Expo)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;desktop&lt;/code&gt; - Desktop apps (Neutralino)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;website&lt;/code&gt; - Web app (Next.js)&lt;/li&gt;
&lt;li&gt;…&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/julien040/lotir" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Additional Resources / Info
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What's the use case of the app ? 🤔
&lt;/h3&gt;

&lt;p&gt;Lotir permites an easy share of text and image between your phone and your computer. It takes less than 30 seconds to share. And it's bi-directionnal. You can share from your computer or your phone.&lt;br&gt;
Lotir can be useful in these situations :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Imagine you receive a long OTP Code on your phone. Use Lotir to send it to your computer.&lt;/li&gt;
&lt;li&gt;You have a very long url that you should send to Whatsapp. Use Lotir to send it to your phone.&lt;/li&gt;
&lt;li&gt;You have a photo on your phone, and you need it on your computer. Send it over Lotir.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also, everything is temporary. Thanks to &lt;a href="https://docs.mongodb.com/manual/core/index-ttl/" rel="noopener noreferrer"&gt;MongoDB TTL indexes&lt;/a&gt;, each thing shared stays on the server for 7 days.&lt;/p&gt;
&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Share any text from and to your phone/computer&lt;/li&gt;
&lt;li&gt;Share any image from and to your phone/computer&lt;/li&gt;
&lt;li&gt;Receive a notification when a new share happened&lt;/li&gt;
&lt;li&gt;Data is safe and temporary. I use a MongoDB Database hosted on Atlas. Data is replicated using a replica-set in three availability zones and can't stay for more than 7 days.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  How to use ?
&lt;/h3&gt;

&lt;p&gt;Lotir does not have the concept of registration/login. Instead, it uses a sync key. Consider it as a secret key, it authenticates you to the server. Keep it hidden. To use Lotir, just put the same key in all your devices.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffglubv32wqs681an1lm4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffglubv32wqs681an1lm4.png" alt="How to set sync key"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you done that, you're ready to use Lotir&lt;/p&gt;
&lt;h3&gt;
  
  
  Download app
&lt;/h3&gt;

&lt;p&gt;As you saw, Lotir is composed of two parts : a computer app and a phone app.&lt;/p&gt;
&lt;h4&gt;
  
  
  Computer
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://lotir.ml" rel="noopener noreferrer"&gt;Lotir.ml&lt;/a&gt; is a web app that can handle all computer functionalities. However, if you prefer desktop apps, you can download them from &lt;a href="https://github.com/julien040/lotir/releases/tag/v1.0.0" rel="noopener noreferrer"&gt;github release&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo4ym3kref2tma3sl13d6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo4ym3kref2tma3sl13d6.png" alt="Website Home"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Phone
&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;Currently, iOS is not yet available.&lt;/em&gt;&lt;br&gt;
To use Lotir on your phone, install its APK from &lt;a href="https://github.com/julien040/lotir/releases/tag/v1.0.0" rel="noopener noreferrer"&gt;github release&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Google Play protect may show several warnings about the security of this app. You can skip them.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvmv3p247qfhh76dbt6sn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvmv3p247qfhh76dbt6sn.png" alt="Lotir on android"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Architecture 🗺
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmdqxkd1yrg2pxmaathrm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmdqxkd1yrg2pxmaathrm.png" alt="Lotir architecture"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Clients (blue on diagram)
&lt;/h4&gt;

&lt;p&gt;Clients are mobile apps, desktop apps and web apps. They identify themselves using the sync key specified in settings. Using Realm SDK (web and React Native), they interact with MongoDB Realm Functions.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Web app is built using NextJS and Realm Web SDK. UI is handled by chakra UI. I've added a custom theme on top of it.&lt;/li&gt;
&lt;li&gt;Mobile apps are built using Expo and NativeBase. Notification service is also handled by Expo. Apps are then built using EAS Build.&lt;/li&gt;
&lt;li&gt;Desktop apps are just a webview around &lt;a href="https://lotir.ml" rel="noopener noreferrer"&gt;Lotir.ml&lt;/a&gt; created with Neutralino. These are lighter than Electron.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Realm Function (purple on diagram)
&lt;/h4&gt;

&lt;p&gt;Those are the main interface between client and backend. There is 5 of them :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;addMessage : used to share text&lt;/li&gt;
&lt;li&gt;addImage : used to share image&lt;/li&gt;
&lt;li&gt;setToken : used to register notification token for mobile app&lt;/li&gt;
&lt;li&gt;getMessages : retrieve shared text and images for clients&lt;/li&gt;
&lt;li&gt;notifMobile : a function that responds to Realm trigger to create a new notifications&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;When an image needs to be shared, it is first upload to imgBB by the &lt;code&gt;addImage&lt;/code&gt; function and link is then saved using &lt;code&gt;addMessage&lt;/code&gt; function&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;
  
  
  Database trigger (yellow on diagram)
&lt;/h4&gt;

&lt;p&gt;Each time something is shared, a new document is created in MongoDB. Creation of a new document triggers the notifMobile function. If it is detected that the message comes from a computer, the function sends a notification to the phone.&lt;/p&gt;
&lt;h4&gt;
  
  
  MongoDB Atlas database (green on diagram)
&lt;/h4&gt;

&lt;p&gt;This is the central piece of the architecture. The MongoDB database hosted on MongoDB Atlas saves and retrieves all data for Realm Functions. Each share is saved in a collection with 5 attributes :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;platform : mobile or computer&lt;/li&gt;
&lt;li&gt;id : the sync key&lt;/li&gt;
&lt;li&gt;title : &lt;em&gt;self explanatory&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;body : &lt;em&gt;self explanatory&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;time : timestamp of when the share was created&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then, a MongoDB TTL index ensures that shares don't stay more than 7 days on the server.&lt;/p&gt;
&lt;h4&gt;
  
  
  MongoDB Charts
&lt;/h4&gt;

&lt;p&gt;To check some stats about the app, I've set up a small dashboard using MongoDB Charts. I can see how many different users there are, the repartition per platform and how many shares have been made in a week.&lt;br&gt;
&lt;a href="https://charts.mongodb.com/charts-hackathon-yozau/public/dashboards/7229043c-49a4-49d0-9010-efdfe1cce2c9" rel="noopener noreferrer"&gt;Link to dashboard&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  MongoDB Realm Hosting
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://lotir.ml" rel="noopener noreferrer"&gt;Lotir.ml&lt;/a&gt; is hosted using MongoDB Realm Hosting&lt;/p&gt;
&lt;h3&gt;
  
  
  Open Source projects used
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Expo : thanks to a recent update, Realm SDK could be used along Expo&lt;/li&gt;
&lt;li&gt;Realm SDK (web and React Native) : those SDKs greatly simplified development and permits me to avoid using http endpoints.&lt;/li&gt;
&lt;li&gt;react-native-async-storage : used to save data in Mobile app&lt;/li&gt;
&lt;li&gt;Native base : Great UI library for React Native&lt;/li&gt;
&lt;li&gt;react-navigation : Router for React Native&lt;/li&gt;
&lt;li&gt;React for NextJs and React Native&lt;/li&gt;
&lt;li&gt;NextJS : the site isn't optimized but still run fast thanks to NextJS&lt;/li&gt;
&lt;li&gt;Chakra UI : Equivalent of Native base but for React and Vue.&lt;/li&gt;
&lt;li&gt;Icons 8 : Icons come from this awesome website&lt;/li&gt;
&lt;li&gt;Neutralino : Embed the web app in executables&lt;/li&gt;
&lt;li&gt;Realm-cli : I could easily push and pull config of Realm Project thanks to CLI.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Demo
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;To record my phone screen, I use &lt;code&gt;Your Phone&lt;/code&gt; from Microsoft&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;These videos show two situations : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Transfering text to phone&lt;/li&gt;
&lt;li&gt;Transfering image to phone&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/48bThHfEfug"&gt;
&lt;/iframe&gt;
&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/ReES1dGLhd4"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>atlashackathon</category>
    </item>
  </channel>
</rss>
