August 25th, 2023 How to build a Reddit Clone with Replit and Adama By Gold Agbonifo Isaac

If you’re a developer who loves using Replit for development, you’re in for a treat! In this article, we’ll walk you through the process of creating your very own Reddit clone using Replit and Adama.

Getting to Know Adama

Before diving into the building process, let’s quickly familiarize ourselves with Adama. If you’ve already read the Adama manual, feel free to skip this section; otherwise, it’s a helpful guide for using Adama in your Reddit clone.

Authentication: Unlocking the Adama Platform

Authentication in Adama is like revealing a secret passcode to enter a secure club. Users need an identity token (JSON Web Token or JWT) signed by Adama or a private key to interact with Adama. Special tokens such as anonymous:AgentSmith enable anonymous internet visitors to access documents.

Spaces: Where Documents Hang Out

Imagine spaces in Adama as special clubs where similar documents hang out together. Each space groups together different documents running the same Adama code. Every document maintains a history of changes, and Adama efficiently organizes and manages these changes using log storage.

Router and the API Space: Connecting with Documents

The Adama router serves as a guide, helping people find and connect with documents. Adama acts as a vast document store, capable of handling countless documents. Each document possesses a unique key and belongs to a specific group known as a “space.”

Documents: The Heart of Adama

Adama documents are substantial JSON files with a comprehensive change history. Developers can modify documents using the Adama language, and changes are bundled into transactions and written into a log. Each document is identified by a unique key within its owning space.

Log Storage: Organizing Changes

Log storage is akin to a colossal journal that diligently records all document changes. It uses special patches to record changes without erasing or rewriting everything. This approach keeps everything organized, reliable, and consistent.


Further Exploration on the Adama Platform

Before we dive into the building process, let’s take a closer look at the key features of the Adama Platform:

  1. Creating Documents: The Adama Platform simplifies the process of creating new documents. Imagine it as crafting new pages for your important files. You can customize access control, information content, and functionality for each document.

  2. Reading Documents: Once a document is created, you can read its content. Reading access is restricted to the document creator, ensuring privacy and security.

  3. Updating Documents: When updates are required, the Adama Platform allows you to send special messages to your documents for updates. It’s similar to adding new information or making changes to your documents. All connected users automatically see these updates, facilitating seamless collaboration.

  4. Deleting Documents: Unnecessary documents can be easily deleted. Think of it as discarding papers you no longer need. Only the document creator can delete documents, maintaining control over document management.

The Adama Platform boasts several remarkable features that enhance its utility:

  • Security and Privacy: Keep your documents secure and limit access to authorized individuals.

  • Serverless: There’s no need for a specialized server setup; Adama handles the complexities, allowing you to focus on development.

  • Versatility: From games to real-time chat systems, Adama empowers developers to create diverse and innovative applications.

In essence, the Adama Platform serves as a valuable tool for creating, reading, updating, and deleting important documents. It prioritizes data security and user-friendliness.

Building Your Reddit Clone

Exciting times are ahead! Let’s roll up our sleeves and commence the construction of our Reddit clone on Replit!

And so, the adventure begins!

Creating a Reddit-like platform doesn’t have to be complicated. Whether you’re a seasoned developer or just starting out, these step-by-step instructions will help you create your own interactive platform.

Step 1: Setting Up Your Replit Environment

The first step is to create your “repl.” In Replit, a “repl” is a short term for “Read-Eval-Print Loop,” and it refers to an interactive programming environment. Here’s how you can get started:

  1. Click the “Create Repl” button on the left side of your Replit page.

Create Repl

  1. Choose a Java template, specifically “Java Swing,” from the available template options.

  2. Give your repl a name, and then click “Create Repl.”

Choose a Template

Congratulations, you’ve successfully created your Replit environment!

Obtaining the Adama JAR File

Before we proceed, we need to obtain the Adama JAR file. Since Replit doesn’t support the wget command, we’ll use curl:

curl -fSLO https://aws-us-east-2.adama-platform.com/adama.jar
java -jar adama.jar

Step 3: Initialize your developer account

Next, you’ll need to set up your developer account in Adama. Follow these steps:

Paste this in your terminal:

java -jar adama.jar init

You’ll be prompted to provide an email address. Use an active one, as a verification code will be sent to your email. Paste this code into your terminal to complete the setup.

init

Step 4: Create a space

Now, let’s create a space. A space is where your Reddit clone will reside. Use this command to create one:

 java -jar adama.jar space create --space redditclone

Please make sure to replace redditclone with your preferred space name.

If you encounter an error message like this:

error message

It may be because you have restarted the session by reloading the page or leaving it. Replit doesn’t save sessions. However, you can follow the steps to initialize your developer account again and then create a space.

java -jar adama.jar init

The ‘java -jar adama.jar init’ is going to be useful whenever you have such error messages in the future tasks ahead. But if you don’t see an error message then you are on the right track and your space is created!

If you are one who loves to poke around your environment then,use:

java -jar adama.jar space help

Step 5: Set Up the Front End (HTML, RxHTML and style sheet)

Now, let’s set up the front end of your Reddit clone using HTML, RxHTML, and a Tailwind CSS stylesheet.

Create an index.html file, and make sure to name it exactly “index.html.” Replit requires this specific name for HTML files. Here’s the code to place within your index.html file

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Reddit Clone</title>
  <link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.16/dist/tailwind.min.css" rel="stylesheet">
</head>
<body class="bg-gray-100">
<header class="bg-white py-4 shadow">
  <div class="container mx-auto px-4">
    <nav class="flex items-center justify-between">
      <a href="#" class="text-2xl font-bold text-gray-800">Clone</a>
      <div>
        <a href="/" class="mr-4 text-gray-600 hover:text-gray-800">Home</a>
        <a href="/popular" class="mr-4 text-gray-600 hover:text-gray-800">Popular</a>
        <a href="/all" class="mr-4 text-gray-600 hover:text-gray-800">All</a>
      </div>
    </nav>
  </div>
  </header>
  <main class="container mx-auto px-4 py-8">
  <div class="flex">
    <div class="w-2/3 mr-4">
      <!-- Main Content -->
      <div class="bg-white p-4 shadow mb-4">
        <h2 class="text-xl font-bold mb-2">Post Title</h2>
        <p class="text-gray-600 mb-4">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
        <div class="flex items-center text-gray-600">
          <span class="mr-2">Posted by John Doe</span>
          <span class="mr-2">1 hour ago</span>
          <span class="mr-2">100 comments</span>
        </div>
      </div>
      <div class="bg-white p-4 shadow mb-4">
        <h2 class="text-xl font-bold mb-2">Another Post Title</h2>
          <p class="text-gray-600 mb-4">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
          <div class="flex items-center text-gray-600">
          <span class="mr-2">Posted by Jane Smith</span>
          <span class="mr-2">2 hours ago</span>
          <span class="mr-2">50 comments</span>
        </div>
      </div>
    </div>
    <div class="w-1/3">
      <!-- Sidebar -->
      <div class="bg-white p-4 shadow">
        <h2 class="text-xl font-bold mb-4">Sidebar</h2>
        <ul class="text-gray-600">
          <li class="mb-2"><a href="#" class="hover:text-gray-800">Category 1</a></li>
          <li class="mb-2"><a href="#" class="hover:text-gray-800">Category 2</a></li>
          <li class="mb-2"><a href="#" class="hover:text-gray-800">Category 3</a></li>
          <li class="mb-2"><a href="#" class="hover:text-gray-800">Category 4</a></li>
        </ul>
      </div>
    </div>
  </div>
  </main>
  <footer class="bg-gray-200 py-4">
    <div class="container mx-auto px-4 text-center">
      <span class="text-gray-600">© 2023 Clone. All rights reserved.</span>
    </div>
   </footer>
</body>
</html>

Step 6: Creating an RxHTML File

  • Now, create an RxHTML file named “frontend.rx.html” and populate it with the following content:
<forest>
  <page uri="/">
    <header class="bg-white py-4 shadow">
  <div class="container mx-auto px-4">
      <nav class="flex items-center justify-between">
        <a href="#" class="text-2xl font-bold text-gray-800">Clone</a>
        <div>
          <a href="/" class="mr-4 text-gray-600 hover:text-gray-800">Home</a>
          <a href="/popular" class="mr-4 text-gray-600 hover:text-gray-800">Popular</a>
          <a href="/all" class="mr-4 text-gray-600 hover:text-gray-800">All</a>
        </div>
      </nav>
    </div>
    </header>
    <main class="container mx-auto px-4 py-8">
    <div class="flex">
      <div class="w-2/3 mr-4">
        <!-- Main Content -->
        <div class="bg-white p-4 shadow mb-4">
          <h2 class="text-xl font-bold mb-2">Post Title</h2>
          <p class="text-gray-600 mb-4">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
          <div class="flex items-center text-gray-600">
            <span class="mr-2">Posted by John Doe</span>
            <span class="mr-2">1 hour ago</span>
            <span class="mr-2">100 comments</span>
          </div>
        </div>
        <div class="bg-white p-4 shadow mb-4">
          <h2 class="text-xl font-bold mb-2">Another Post Title</h2>
            <p class="text-gray-600 mb-4">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
            <div class="flex items-center text-gray-600">
            <span class="mr-2">Posted by Jane Smith</span>
            <span class="mr-2">2 hours ago</span>
            <span class="mr-2">50 comments</span>
          </div>
        </div>
      </div>
      <div class="w-1/3">
        <!-- Sidebar -->
        <div class="bg-white p-4 shadow">
          <h2 class="text-xl font-bold mb-4">Sidebar</h2>
          <ul class="text-gray-600">
            <li class="mb-2"><a href="#" class="hover:text-gray-800">Category 1</a></li>
            <li class="mb-2"><a href="#" class="hover:text-gray-800">Category 2</a></li>
            <li class="mb-2"><a href="#" class="hover:text-gray-800">Category 3</a></li>
            <li class="mb-2"><a href="#" class="hover:text-gray-800">Category 4</a></li>
          </ul>
        </div>
      </div>
    </div>
    </main>
    <footer class="bg-gray-200 py-4">
      <div class="container mx-auto px-4 text-center">
        <span class="text-gray-600">© 2023 Clone. All rights reserved.</span>
      </div>    </footer>
    </page>
    <!-- the shell generates the 200.html used for the SPA -->
    <shell inline=true body-class="bg-gray-100">
      <link rel="stylesheet" href="/style.css">
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <meta charset="UTF-8">
    </shell>
  </forest>

Step 7: Setting Up the Stylesheet

Create an “input.css” file and add the following content:

  @tailwind base;
  @tailwind components;
  @tailwind utilities;

Additionally, create a package.json file with the following content:

  {
    "devDependencies": {
      "@tailwindcss/forms": "^0.4.0",
      "@tailwindcss/typography": "^0.5.1",
      "tailwindcss": "^3.0.19"
    }
  }

Install Tailwind CSS using:

npm install

To make your page live, create a “ship.sh” file with the following content:

#!/bin/sh
clear
npx tailwindcss -i input.css --content frontend.rx.html -o style.css
java -jar adama.jar space upload --space redditclone --file style.css
java -jar adama.jar space set-rxhtml --space redditclone --file frontend.rx.html

And rememeber to update the space name.

Run the ship.sh file with:

chmod 755 ship.sh
./ship.sh

Please make sure ypu replace rclone with your space name. To view the live version, enter “YourSpaceName.adama.games” in your browser. For example, if your space name is “myredditapplication,” type “myredditapplication.adama.games.”

The finale page

Step 8: Adding a Comment Submission Form

we need a way for users to submit comments. Let’s set up the necessary data structures in the backend.adama file:

record Comment {
  public int id;
  public principal author;
  public string comment;
  public datetime when;
}

record Submission {
  public int id;
  public string title;
  public string description;
  public string url;
  public string category;
  public datetime when;
  public principal submitter;

  table<Comment> _comments;
  public formula comments = iterate _comments;
}

table<Submission> _submissions;

To deploy these changes, update the ship.sh file by adding:

java -jar adama.jar space deploy --space redditclone --file backend.adama

Step 9: Create an Html Template for the Comment Submission Form.

Now, let’s design an HTML template for the comment submission form, similar to Reddit’s style, with fields for a title, URL, description, and category.

 <!-- header removed -->
  <main class="container mx-auto px-4 py-8">
    <div class="max-w-lg mx-auto bg-white p-6 shadow">
      <h2 class="text-2xl font-bold mb-4">Submit a Post</h2>
      <form>
        <div class="mb-4">
          <label for="title" class="block text-gray-700 font-bold mb-2">Title</label>
          <input type="text" id="title" name="title" class="w-full border border-gray-400 p-2 rounded focus:outline-none focus:border-indigo-500" required>
        </div>
        <div class="mb-4">
          <label for="description" class="block text-gray-700 font-bold mb-2">Description</label>
          <textarea id="description" name="description" class="w-full border border-gray-400 p-2 rounded focus:outline-none focus:border-indigo-500" required></textarea>
        </div>
        <div class="mb-4">
          <label for="url" class="block text-gray-700 font-bold mb-2">URL</label>
          <input type="url" id="url" name="url" class="w-full border border-gray-400 p-2 rounded focus:outline-none focus:border-indigo-500" required>
        </div>
        <div class="mb-4">
          <label for="category" class="block text-gray-700 font-bold mb-2">Category</label>
          <input type="category" id="category" name="category" class="w-full border border-gray-400 p-2 rounded focus:outline-none focus:border-indigo-500" required>          
        </div>
        <div class="text-right">
          <button type="submit" class="px-4 py-2 bg-indigo-500 text-white font-semibold rounded hover:bg-indigo-600">Submit</button>
        </div>
      </form>
    </div>
  </main>
  <!-- footer removed -->

To improve code reusability, you can encapsulate the header and footer elements into a template. Create a <template name="nav"> section and use it for both the root and post pages.

Here’s the template:

<template name="nav">
  <header class="bg-white py-4 shadow">
    <div class="container mx-auto px-4">
      <nav class="flex items-center justify-between">
        <a href="/" class="text-2xl font-bold text-gray-800">Clone</a>
        <div>
          <a href="/" class="mr-4 text-gray-600 hover:text-gray-800">Home</a>
          <a href="/post" class="mr-4 text-gray-600 hover:text-gray-800">Post</a>
        </div>
      </nav>
    </div>
  </header>
  <fragment />
  <footer class="bg-gray-200 py-4">
    <div class="container mx-auto px-4 text-center">
      <span class="text-gray-600">© 2023 Clone. All rights reserved.</span>
    </div>
  </footer>
</template>

Then, update your pages as follows:

<page uri="/">
    <div rx:template="nav">
      <main class="container mx-auto px-4 py-8">
      ...
      </main>
    </div>
  </page>
  <page uri="/post">
    <div rx:template="nav">
      <main class="container mx-auto px-4 py-8">
      ...
      </main>
    </div>
  </page>

To establish connections and allow document creation, update the backend.adama file with the following code at the beginning:

static {
  create {
    return true; // anyone can create it
  }
  invent {
    return true; // anyone can invent
  }
}

@connected {
  return true;
}

This code allows connections to a document and allows the document to be created on demand. Now, let’s connect to a document by updating the nav template. Wrap the <fragment /> with <connection>:

 ...
  </header>
  <connection identity="direct:anonymous:anony" space="rclone" key="somekey">
    <fragment />
  </connection>
  <footer class="bg-gray-200 py-4">
  ...

Connect the form to data by creating a channel named “submit_post” and adding this code to the backend.adama file:

message SubmissionMsg {
  string title;
  string description;
  string url;
  string category;
}

channel submit_post(SubmissionMsg s) {
  _submissions <- {
    title: s.title,
    description: s.description,
    url: s.url,
    category: s.category,
    when: Time.datetime(),
    submitter: @who
  };
}

Finally, link the form on the “/post” page to the “submit_post” channel by adding an rx:action attribute to the form element:

...
<h2 class="text-2xl font-bold mb-4">Submit a Post</h2>
<form rx:action="send:submit_post" rx:success="goto:/">
   <div class="mb-4">
...

Update the backend.adama file with the following formula to retrieve the latest 20 posts:

public formula posts = iterate _submissions limit 20;

Let’s edit the root page by templatizing the post listing. Here’s the updated code for listing posts:

<div class="w-2/3 mr-4" rx:iterate="posts">
  <!-- Main Content -->
  <div class="bg-white p-4 shadow mb-4">
    <h2 class="text-xl font-bold mb-2"><a href="/v/{category}/{id}"><lookup path="category" />/<lookup path="title" /></a></h2>
    <p class="text-gray-600 mb-4"><lookup path="description" /></p>
    <div class="flex items-center text-gray-600">
      <span class="mr-2">Posted by <lookup path="submitter" transform="principal.agent" /></span>
      <span class="mr-2"><lookup path="when"></span>
      <span class="mr-2"><lookup path="num_comments"> comments:</span>
    </div>
  </div>
</div>

Step 10: Implementing Categories and Viewing Posts by Category

To make categories work, we’ll add a new message and procedure in the backend.adama file as follows:

message CategoryReport {
  string category;
  int count;
}

procedure make_categories() -> list<CategoryReport> readonly {
  var m = iterate _submissions reduce category via @lambda x: x.size();
  table<CategoryReport> report;
  foreach (kvp in m) {
    report <- {category: kvp.key, count:kvp.value};
  }
  return iterate report;
}

This code defines a message to report category counts and a procedure to calculate and return category statistics.

Next, update the root page template to iterate through the categories. Create another HTML template for displaying Reddit posts by category, including the title, description, and comments, along with a form to post a comment:

<template name="post">
  <div class="bg-white p-4 shadow mb-4">
    <h2 class="text-xl font-bold mb-2"><lookup path="category" />::<lookup path="title" /></h2>
    <p class="text-gray-600 mb-4"><lookup path="description" /></p>
    <div class="flex items-center text-gray-600">
      <span class="mr-2">Posted by <lookup path="submitter" transform="principal.agent" /></span>
      <span class="mr-2">1 hour ago [TODO]</span>
      <span class="mr-2">100 comments: [TODO]</span>
    </div>
  </div>
</template>

Now, create a new page with a dynamic URI to view posts in a specific category:

<page uri="/c/$current_category:text">
  <div rx:template="nav">
  <main class="container mx-auto px-4 py-8">
    <div class="flex">
      <div class="w-full mr-4" rx:iterate="posts">
        <div rx:template="post"></div>
      </div>
    </div>
  </main>
  </div>
</page>

Notice, the uri has a parameter which is stored in the view state. The view state is sent with a connection, and we can change the posts field to a bubble which can leverage the viewer state.(There’s something special called a “parameter” ($current_category:text). We save this parameter in something called the “view state.” When we send information to the website, we include this view state. And instead of using a regular field for posts, we’re going to use something called a “bubble” that works with the viewer state.)

view string current_category;
bubble posts = 
  iterate _submissions
  where  @viewer.current_category == category || @viewer.current_category == ""
  limit 20;

Incorporate these changes into the root page by adding the following line:

<div rx:template="nav" rx:load="set:current_category=">

Now, let’s create a new HTML template for Reddit posts showing the title, description, and comments, along with a form to post a comment. This template will be used to display a single post with its comments:

<main class="container mx-auto px-4 py-8">
    <div class="max-w-lg mx-auto bg-white p-6 shadow">
      <h2 class="text-2xl font-bold mb-4">Post Title</h2>
      <p class="text-gray-600 mb-4">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>

      <div class="mb-4">
        <h3 class="text-xl font-bold mb-2">Comments</h3>
        <div class="bg-gray-200 rounded-lg p-4">
          <div class="mb-4">
            <div class="flex items-start">
              <div class="w-10 h-10 rounded-full bg-gray-400"></div>
              <div class="ml-2">
                <span class="font-bold">John Doe</span>
                <span class="text-gray-600">1 hour ago</span>
              </div>
            </div>
            <p class="mt-2 text-gray-800">Comment 1</p>
          </div>

          <div class="mb-4">
            <div class="flex items-start">
              <div class="w-10 h-10 rounded-full bg-gray-400"></div>
              <div class="ml-2">
                <span class="font-bold">Jane Smith</span>
                <span class="text-gray-600">2 hours ago</span>
              </div>
            </div>
            <p class="mt-2 text-gray-800">Comment 2</p>
          </div>
        </div>
      </div>

      <div class="mb-4">
        <h3 class="text-xl font-bold mb-2">Post a Comment</h3>
        <form>
          <div class="mb-4">
            <label for="name" class="block text-gray-700 font-bold mb-2">Name</label>
            <input type="text" id="name" name="name" class="w-full border border-gray-400 p-2 rounded focus:outline-none focus:border-indigo-500" required>
          </div>
          <div class="mb-4">
            <label for="comment" class="block text-gray-700 font-bold mb-2">Comment</label>
            <textarea id="comment" name="comment" class="w-full border border-gray-400 p-2 rounded focus:outline-none focus:border-indigo-500" required></textarea>
          </div>
          <div class="text-right">
            <button type="submit" class="px-4 py-2 bg-indigo-500 text-white font-semibold rounded hover:bg-indigo-600">Submit</button>
          </div>
        </form>
      </div>

Now, connect this to the /v/$category/$current_post route by exposing the current post using the viewer state and a channel to write comments:

view int current_post_id;
bubble current_post =
  (iterate _submissions where id == @viewer.current_post_id)[0];

message WriteComment {
  int id;
  string comment;
}

channel write_comment(WriteComment wc) {
  if( (iterate _submissions where id == wc.id)[0] as sub) {
    sub._comments <- {
        author: @who,
        comment: wc.comment, 
        when: Time.datetime()
    };
  }
}

With this implementation, you can view posts by category and view individual post pages with comments and a comment submission form.

FINALE:

This is the very last part of our tutorial! We’re creating a page where users can see a post and the comments on it. Here’s the code you need for that:

<main class="container mx-auto px-4 py-8" rx:scope="current_post">
  <div class="max-w-lg mx-auto bg-white p-6 shadow">
    <h2 class="text-2xl font-bold mb-4"><lookup path="title" /></h2>
    <p class="text-gray-600 mb-4"><lookup path="description" /></p>
    <div class="mb-4">
    <h3 class="text-xl font-bold mb-2">Comments</h3>
    <div class="bg-gray-200 rounded-lg p-4" rx:iterate="comments">
    <div class="mb-4">
      <div class="flex items-start">
        <div class="w-10 h-10 rounded-full bg-gray-400"></div>
          <div class="ml-2">
              <span class="font-bold">
                <lookup path="author" transform="principal.agent" />
              </span>
              <span class="text-gray-600"><lookup path="when"></span>
            </div>
          </div>
          <p class="mt-2 text-gray-800"><lookup path="comment"></p>
        </div>
      </div>
    </div>
  </div>
  <div class="mb-4">
    <h3 class="text-xl font-bold mb-2">Post a Comment</h3>
    <form rx:action="send:write_comment" rx:success="reset">
      <input type="hidden" name="id" value="{view:current_post_id}" />
      <div class="mb-4">
        <label for="comment" class="block text-gray-700 font-bold mb-2">Comment</label>
        <textarea id="comment" name="comment" class="w-full border border-gray-400 p-2 rounded focus:outline-none focus:border-indigo-500" required></textarea>
      </div>
      <div class="text-right">
        <button type="submit" class="px-4 py-2 bg-indigo-500 text-white font-semibold rounded hover:bg-indigo-600">Submit</button>
      </div>
    </form>
  </div>
</main>

View the updates by going back to your browser and typing ‘yourspacename.adama.games’ .

Conclusion: Congratulations! You’ve successfully created a basic Reddit-like page where users can view and comment on posts. Adama makes it easy to focus on building the front end without worrying about the complexities of the back end. If you enjoyed this, you can explore more examples of what you can build with Adama here!