Creating RedTok, the ultimate slop content app with the T3 stack

Creating RedTok, the ultimate slop content app with the T3 stack

Welcome, fellow developers, to another thrilling adventure in the realm of app development! Today, we are diving headfirst into the wild world of TikTok clones. But wait, hold your horses! This is not just any ordinary TikTok clone. No, no, my friends. We are taking it up a notch by fetching posts from Reddit, adding a Text-to-Speech (TTS) speaker to read the titles, and throwing in a dash of Subway Surfers for good measure, basically creating the ultimate slop machine (video completely unrelated), or just everyone's "For You Page" feed on TikTok in the last couple of months. Fasten your seatbelts, folks; it's going to be a wild ride 🎢!

Why I chose Expo (or more specifically the T3 Stack) 🚀

We're diving into this adventure armed with the Expo app, powered by the T3 stack. Now, what's the T3 stack, you ask? Well, it's like the Avengers of tech stacks, combining the powers of React Native, TypeScript, TailwindCSS (with Nativewind), and tRPC (which we will not use anyway). With this mighty trio at our disposal, we're ready to conquer new horizons in app development.

But let's not forget our trusty sidekick, Reddit. We'll be fetching posts from the subreddit of our choice, be it "r/popular," "r/funny," or even "r/pyrocynical". The possibilities are endless! ... well except the fact that most reddit slop videos are about the r/nosleep and r/twosentencehorror communities. We'll be harnessing anyway the power of the Reddit API to bring the most viral, hilarious, and sometimes downright bizarre content to our users.

What's in Our Toolkit? 🔧

Before we dive into the nitty-gritty details, let's take a quick look at the tools we'll be using to create this marvel of an app:

  • Expo: Our go-to framework for building delightful cross-platform apps with React Native. It's like a Swiss Army knife for developers, packed with powerful features and an incredibly smooth development experience.
  • T3 Stack: Our secret sauce! The combination of TypeScript and TailwindCSS gives us superpowers to create user interfaces and maintainable code in seconds. Plus, it's like having a personal cheerleader, constantly reminding us to write clean and scalable code.
  • Reddit's public API: The gateway to the fascinating world of Reddit. Thankfully Reddit is not charging people for the public API (give it a couple of months and that may change tho). We'll allow the user to fetch posts from various subreddits and bringing them to life in our app. It's like opening a Pandora's box of internet weirdness!
  • Expo-AV (Text-To-Speech): Say goodbye to reading, my friends. Now, you can sit back, relax, and let the TTS speaker serenade you with the dulcet tones of the internet's finest content /s.
  • Subway Surfers: Yes, you read that right. We're adding a touch of Subway Surfers to our app. Because why not? Would you rather prefer Minecraft? That's not slop enough for our glorious app.

What will we be coding? 📱

In this exciting coding adventure, we'll be focusing on building the core features of our TikTok-like app with Reddit posts, TTS, and the background video. Let's take a closer look at what we'll be coding:

Fetching the Current Post and More Posts 📥

Our first task is to fetch the current Reddit post and load more posts as the user scrolls through the app. We'll use the power of the Reddit API to retrieve the necessary data, such as the post title, URL, thumbnail, subreddit, author, and body. By leveraging the fetch function and handling JSON responses, we'll transform the raw data into a format that's easy to work with.

Creating the Main Screen 🚀

Next, we'll design the main screen of our app. This screen will showcase the Reddit post, along with the TikTok-inspired elements. We'll keep the familiar TikTok bottom bar, the "For You" and "Following" sections on top, the search bar (which is just a fancy way to change subreddit), and the "likes" (upvotes), "comments", "bookmarks" (awards), and "share" (useless) buttons on the side, to maintain that authentic TikTok vibe. However, we'll make these elements unaccessible since our app is all about Reddit goodness.

Adding Text-to-Speech (TTS) and Background Video 🎙️

To "enhance" the user experience, we'll incorporate the Text-to-Speech (TTS) functionality, which will read the title of the Reddit post aloud. This way, users can sit back, relax, and listen to the post title instead of reading it. Since that's clearly not enough to keep his attention, we will also add the subway surfers's gameplay video underneath at maximum volume, so that he will not close the app in less than 3 seconds.

The reddit post on top 🎨

Since we can't just screenshot the desktop page on the app, we will have to be creative and create an imitation of it. I just decided to put a semiopaque area on top with the author name, the date of the post, and the content, but you can be more creative in case you want to waste more time on this project.

Let the Magic Begin: Fetching Reddit Posts 📚

To fetch the Reddit posts, we'll be tapping into the Reddit API's vast treasure trove of content. We'll use our trusty TypeScript skills to write functions that fetch posts from the desired subreddit. With some JSON magic and a sprinkle of Promises, we'll transform the raw data into a more manageable format for our app.

Here is the code for my src/utils/reddit.ts code:

export async function getRedditFrontPagePosts(subreddit = "popular") {
  return await fetch(`https://www.reddit.com/r/${subreddit}.json`)
    .then((res) => res.json())
    .then((data) => {
      return data.data.children.map((post: any) => {
        return {
          id: post.data.id,
          title: post.data.title,
          url: post.data.url,
          thumbnail: post.data.thumbnail,
          subreddit: post.data.subreddit,
          author: post.data.author,
          body: post.data.text ?? post.data.selftext ?? post.data.body,
          total_awards_received: post.data.total_awards_received,
          score: post.data.score,
          ups: post.data.ups,
          num_comments: post.data.num_comments,
          created_at: new Date(post.data.created_utc * 1000),
        };
      });
    });
}

export async function loadMorePosts(subreddit = "popular", after = "") {
  return await fetch(
    `https://www.reddit.com/r/${subreddit}.json?after=${after}?limit=10`
  )
    .then((res) => res.json())
    .then((data) => {
      return data.data.children.map((post: any) => {
        return {
          id: post.data.id,
          title: post.data.title,
          url: post.data.url,
          thumbnail: post.data.thumbnail,
          subreddit: post.data.subreddit,
          author: post.data.author,
          body: post.data.text ?? post.data.selftext ?? post.data.body,
          total_awards_received: post.data.total_awards_received,
          score: post.data.score,
          ups: post.data.ups,
          num_comments: post.data.num_comments,
          created_at: new Date(post.data.created_utc * 1000),
        };
      });
    });
}

I was unsure whether to use the "score" (upvotes - downvotes) or the "ups" (upvotes only) on the home screen, so I simply decided to put both values on this function and change it later.

Now for the Home Screen, I will happily admit that I got lazy: I started putting position: absolute elements after a while because I couldn't be bothered anymore (I have slop content to watch, you know?).

So anyway, here is the code for the home page (src/app/index.tsx):

import React, { useState, useEffect, useCallback, useRef } from 'react';
import { Dimensions, View, TextInput, FlatList, Text, SafeAreaView, ScrollView, TouchableHighlight } from 'react-native';

import Svg, {Path} from "react-native-svg";
import * as Speech from 'expo-speech';

import { getRedditFrontPagePosts, loadMorePosts } from '~/utils/reddit';
import RedditCard from '~/components/post';


const HEADER_HEIGHT = 60;
const APPBAR_HEIGHT = 130;

const MainPage = () => {
  const [loading, setLoading] = useState(true);
  const [subreddit, setSubreddit] = useState('twosentencehorror');
  const [subredditInput, setSubredditInput] = useState('twosentencehorror');
  const [posts, setPosts] = useState([]);
  const [after, setAfter] = useState('');
  const [currentPost, setCurrentPost] = useState(0);
  const flatListRef = useRef(null);

  // --[ Yeah, I was this lazy. Try messing with this number or fix this with react-native-safe-area-context]
  const LIST_VIEW_HEIGHT = Dimensions.get("window").height - 100;

  const fetchRedditPosts = async (subreddit) => {
    const newPosts = await getRedditFrontPagePosts(subreddit);
    setPosts(newPosts);
    setLoading(false);
  };

  const fetchMorePosts = async () => {
    setLoading(true);
    const newPosts = await loadMorePosts(subreddit, after);
    setPosts([...posts, ...newPosts]);
    setAfter(newPosts[newPosts.length - 1].id);
  };

  const handleChangeSubreddit = (newSubreddit) => {
    setLoading(true);
    setSubreddit(newSubreddit);
  };

  const renderItem = ({ item, index }) => (
    <RedditCard post={item} height={LIST_VIEW_HEIGHT} isActive={index === currentPost} />
  );

  const readPost = (index) => {
    Speech.stop();

    // Read the post.
    Speech.speak(posts[index].title + ' ' + posts[index].body);
  }

  const handleScroll = (event) => {
    const offsetY = event.nativeEvent.contentOffset.y;
    const visibleIndex = Math.max(0, Math.floor(offsetY / LIST_VIEW_HEIGHT));

    if (visibleIndex !== currentPost) {
      setCurrentPost(visibleIndex);
      readPost(visibleIndex);
    }
  };

  useEffect(() => {
    fetchRedditPosts(subreddit).then(() => {
      setLoading(false);
      readCurrentPost();
    });
  }, [subreddit]);

  return (
    <View className="bg-black">
      <SafeAreaView className="flex flex-col">
          <View className="flex flex-row items-center p-2">
            <TextInput
              placeholder="Enter Subreddit"
              className="bg-neutral-800 border-neutral-900 flex-1 p-2 rounded-md text-white placeholder-white"
              value={subredditInput}
              autoCorrect={false}
              autoCapitalize="none"
              onChangeText={setSubredditInput}
              onSubmitEditing={() => handleChangeSubreddit(subredditInput)}
            />

            <TouchableHighlight onPress={() => handleChangeSubreddit(subredditInput)}>
              <View className="w-12 h-12 flex items-center justify-center">
                <Svg
                  xmlns="http://www.w3.org/2000/svg"
                  height={24}
                  width={24}
                  viewBox="0 0 512 512"
                  fill={'white'}
                >
                  <Path d="M416 208c0 45.9-14.9 88.3-40 122.7l126.6 126.7c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0s208 93.1 208 208zM208 352a144 144 0 1 0 0-288 144 144 0 1 0 0 288z" />
                </Svg>
              </View>
            </TouchableHighlight>
          </View>
          {loading && (
            <View className="flex-1 flex items-center justify-center">
              <Text className="text-white">Loading...</Text>
            </View>
          )}
          {!loading && (
              <FlatList
                ref={flatListRef}
                data={posts}
                renderItem={renderItem}
                keyExtractor={(item) => item.id}
                onEndReached={fetchMorePosts}
                onEndReachedThreshold={0.1}
                showsVerticalScrollIndicator={false}
                snapToAlignment={"start"}
                snapToInterval={LIST_VIEW_HEIGHT}
                decelerationRate={0.0}
                pagingEnabled={true}
                disableIntervalMomentum={true}
                onScroll={handleScroll}
                scrollEventThrottle={16}
                windowSize={10}
                style={{ height: LIST_VIEW_HEIGHT }}
              />
          )}


        {/* [For you page] */}
        <View className={'flex flex-row items-center justify-center absolute top-32 left-0 w-screen space-x-4'}>
          <Text className="text-white text-lg">Following</Text>
          <Text className="text-white text-lg font-bold">For You</Text>
        </View>
        {/* [/For you page] */}

        {/* [Navigator] */}
        <View style={{
          height: APPBAR_HEIGHT,
          }}
          className="flex flex-row items-start justify-around absolute bottom-0 left-0 w-screen bg-black pt-4"
        >
          <View className="flex flex-col items-center justify-center">
            <Svg
              xmlns="http://www.w3.org/2000/svg"
              height={32}
              width={32}
              viewBox="0 0 576 512"
              fill={'white'}
            >
              <Path d="M575.8 255.5c0 18-15 32.1-32 32.1h-32l.7 160.2c0 2.7-.2 5.4-.5 8.1V472c0 22.1-17.9 40-40 40h-16c-1.1 0-2.2 0-3.3-.1-1.4.1-2.8.1-4.2.1H392c-22.1 0-40-17.9-40-40v-88c0-17.7-14.3-32-32-32h-64c-17.7 0-32 14.3-32 32v88c0 22.1-17.9 40-40 40h-55.9c-1.5 0-3-.1-4.5-.2-1.2.1-2.4.2-3.6.2h-16c-22.1 0-40-17.9-40-40V360c0-.9 0-1.9.1-2.8v-69.6H32c-18 0-32-14-32-32.1 0-9 3-17 10-24L266.4 8c7-7 15-8 22-8s15 2 21 7l255.4 224.5c8 7 12 15 11 24z" />
            </Svg>
            <Text className={'text-white'}>Home</Text>
          </View>

          <View className="flex flex-col items-center justify-center">
          <Svg
              xmlns="http://www.w3.org/2000/svg"
              height={32}
              width={32}
              viewBox="0 0 512 512"
              fill={'white'}
            >
              <Path d="M416 208c0 45.9-14.9 88.3-40 122.7l126.6 126.7c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0s208 93.1 208 208zM208 352a144 144 0 1 0 0-288 144 144 0 1 0 0 288z" />
            </Svg>
            <Text className={'text-white'}>Discover</Text>
          </View>

<View className="h-10 w-24 rounded-lg flex items-center justify-center bg-white" style={{
  marginTop: 2,
}}>
            <Text className="text-2xl text-black font-bold">+</Text>
          </View>

          <View className="flex flex-col items-center justify-center">
            <Svg
              xmlns="http://www.w3.org/2000/svg"
              height={32}
              width={32}
              viewBox="0 0 512 512"
              fill={'white'}
            >
              <Path d="M160 368c26.5 0 48 21.5 48 48v16l72.5-54.4c8.3-6.2 18.4-9.6 28.8-9.6H448c8.8 0 16-7.2 16-16V64c0-8.8-7.2-16-16-16H64c-8.8 0-16 7.2-16 16v288c0 8.8 7.2 16 16 16h96zm48 124-.2.2-5.1 3.8-17.1 12.8c-4.8 3.6-11.3 4.2-16.8 1.5s-8.8-8.2-8.8-14.3v-80H64c-35.3 0-64-28.7-64-64V64C0 28.7 28.7 0 64 0h384c35.3 0 64 28.7 64 64v288c0 35.3-28.7 64-64 64H309.3L208 492z" />
            </Svg>
            <Text className={'text-white'}>Inbox</Text>
          </View>

          <View className="flex flex-col items-center justify-center">
            <Svg
              xmlns="http://www.w3.org/2000/svg"
              viewBox="0 0 448 512"
              height={32}
              width={32}
              fill={'white'}
            >
              <Path d="M304 128a80 80 0 1 0-160 0 80 80 0 1 0 160 0zm-208 0a128 128 0 1 1 256 0 128 128 0 1 1-256 0zM49.3 464h349.4c-8.9-63.3-63.3-112-129-112h-91.4c-65.7 0-120.1 48.7-129 112zM0 482.3C0 383.8 79.8 304 178.3 304h91.4c98.5 0 178.3 79.8 178.3 178.3 0 16.4-13.3 29.7-29.7 29.7H29.7C13.3 512 0 498.7 0 482.3z" />
            </Svg>
            <Text className={'text-white'}>Me</Text>
          </View>
        </View>
        {/* [/Navigator] */}
      </SafeAreaView>
    </View>
  );
};

export default MainPage;

I also didn't like that by default T3 shows the header, so I updated my src/app/_layout.tsx file to this:

import React from "react";
import { SafeAreaProvider } from "react-native-safe-area-context";
import { Stack } from "expo-router";
import { StatusBar } from "expo-status-bar";

const RootLayout = () => {
  return (
      <SafeAreaProvider>
        <Stack
          screenOptions={{
            headerShown: false,
          }}
        />
        <StatusBar />
      </SafeAreaProvider>
  );
};

export default RootLayout;

I also created the src/components/ folder, with two components inside:

  1. post.tsx which contains the post's content and...
  2. author.tsx, which is a child of post.tsx Don't mind my impecable code, this app was made in 4 hours btw.

Here is the post.tsx code:

import React from 'react';
import { View, Text, StyleSheet, Animated, Image } from 'react-native';
import {Video} from 'expo-av';
import AuthorInfoComponent from '~/components/author';
import Svg, { Path } from 'react-native-svg';


export default function RedditCard({ post, height, isActive }) {
  return (
    <View className={'w-full bg-black relative'} style={{ height: height }}>
      {/* [Background video] */}
      <Video
        source={require('../assets/videos/subway_surfers_background_video.mp4')}
        className="absolute top-0 left-0 w-full h-full"
        shouldPlay={isActive}
        isLooping={isActive}
        volume={1}
        muted={true}
        resizeMode="cover"
      />
      {/* [/Background video] */}

      {/* [Post content] */}
      <View className={'absolute top-8 left-[10vw] p-4 bg-black opacity-80 w-[80vw]'}>
        <AuthorInfoComponent username={post.author} profilePic={post.profilePic} createdAt={post.created_at} />
        <Text className={'text-white text-lg font-bold'}>{post.title}</Text>
        <Text className={'text-white text-sm font-normal mt-2'}>{post.body}</Text>
        {/* Render other post details as needed */}
      </View>
      {/* [/Post content] */}

      {/* [Author info] */}
      <View className="w-[80vw] h-[20vh] absolute bottom-32 left-[5vw] flex flex-col items-start justify-end">
        <Text className="font-semibold text-lg text-white">{post.author}</Text>
        <Text className="font-normal text-sm text-white">{post.body.substring(20)}</Text>
      </View>
      {/* [/Author info] */}

      {/* [CTA buttons] */}
      <View className="flex flex-col absolute bottom-32 right-4 space-y-4 items-center justify-end">
        {/* [Author photo w/ subscribe button] */}
        <View className="rounded-full bg-blue-600 p-2">
          <Svg
            xmlns="http://www.w3.org/2000/svg"
            width={32}
            height={32}
            viewBox="-4.771 0.104 53.521 44.858"
          >
            <Path
              fill="#FFF"
              d="M29.909 35.89c-1.999 1.997-5.218 2.382-7.921 2.382-2.7 0-5.922-.385-7.918-2.382M36.021 4.276 25.899 1.894l-3.93 11.996L25.9 1.894m18.241 3.201a3.99 3.99 0 1 1-7.98 0 3.991 3.991 0 0 1 7.98 0zm.661 23.906c0 8.262-10.263 14.961-22.922 14.961-12.66 0-22.922-6.698-22.922-14.961 0-8.262 10.262-14.961 22.922-14.961 12.659 0 22.922 6.698 22.922 14.961zM-.744 26.676a5.061 5.061 0 0 1-3.027-4.636 5.06 5.06 0 0 1 8.935-3.257m33.568.103a5.061 5.061 0 0 1 9.018 3.154 5.064 5.064 0 0 1-3.23 4.72"
            />
            <Path d="M21.879 44.963c-13.191 0-23.922-7.16-23.922-15.961 0-.608.051-1.21.151-1.801a6.066 6.066 0 0 1-2.879-5.161 6.068 6.068 0 0 1 6.06-6.061c1.493 0 2.916.546 4.017 1.522 4.149-2.663 9.73-4.339 15.887-4.455L25.235.71l.882.208.021.005 9.421 2.218A5 5 0 0 1 40.151.105a4.996 4.996 0 0 1 4.99 4.991 4.996 4.996 0 0 1-4.99 4.99 4.995 4.995 0 0 1-4.99-4.984l-8.596-2.024-3.273 9.99c5.933.231 11.291 1.912 15.291 4.517a6.028 6.028 0 0 1 4.108-1.605 6.068 6.068 0 0 1 6.061 6.061 6.019 6.019 0 0 1-3.08 5.28c.087.553.132 1.113.132 1.681-.002 8.801-10.734 15.961-23.925 15.961zM.157 27.11a9.05 9.05 0 0 0-.2 1.892c0 7.699 9.834 13.961 21.922 13.961 12.088 0 21.922-6.263 21.922-13.961 0-.612-.062-1.215-.183-1.807a1.003 1.003 0 0 1-.099-.435c-.669-2.627-2.494-5.012-5.13-6.934a.992.992 0 0 1-.429-.304c-4.007-2.755-9.732-4.482-16.081-4.482-6.285 0-11.961 1.693-15.962 4.401a1.022 1.022 0 0 1-.401.279C2.823 21.643.951 24.044.256 26.694a.992.992 0 0 1-.084.384c-.005.011-.009.022-.015.032zm40.097-8.319c2.319 1.855 4.021 4.064 4.891 6.488a4.033 4.033 0 0 0 1.605-3.239 4.065 4.065 0 0 0-4.061-4.061 4.04 4.04 0 0 0-2.435.812zm-38.965-.812a4.065 4.065 0 0 0-4.06 4.061c0 1.213.54 2.34 1.436 3.1.899-2.405 2.618-4.596 4.946-6.433a4.066 4.066 0 0 0-2.322-.728zM40.15 2.104c-1.648 0-2.99 1.342-2.99 2.991s1.342 2.99 2.99 2.99 2.99-1.341 2.99-2.99-1.341-2.991-2.99-2.991zM21.988 39.271c-4.005 0-6.827-.875-8.626-2.675a1 1 0 0 1 1.415-1.414c1.405 1.405 3.763 2.089 7.211 2.089 3.447 0 5.807-.684 7.214-2.089a.999.999 0 1 1 1.413 1.414c-1.801 1.8-4.622 2.675-8.627 2.675z" />
            <Path
              fill="#FF4500"
              d="M30.097 22.35c-2.038 0-3.749 1.707-3.749 3.745 0 2.037 1.711 3.688 3.749 3.688s3.688-1.651 3.688-3.688c0-2.038-1.651-3.745-3.688-3.745zm-16.158 0c-2.036 0-3.745 1.709-3.745 3.745s1.708 3.688 3.745 3.688 3.688-1.652 3.688-3.688-1.652-3.745-3.688-3.745z"
            />
          </Svg>
        </View>
        {/* [/Author photo w/ subscribe button] *}

        {/* [Number of likes] */}
        <View className="flex flex-col items-center justify-center space-x-2">
          <Svg
              xmlns="http://www.w3.org/2000/svg"
              height={32}
              width={32}
              viewBox="0 0 512 512"
              fill={"white"}
            >
              <Path d="m225.8 468.2-2.5-2.3L48.1 303.2C17.4 274.7 0 234.7 0 192.8v-3.3c0-70.4 50-130.8 119.2-144 39.4-7.6 79.7 1.5 111.8 24.1 9 6.4 17.4 13.8 25 22.3 4.2-4.8 8.7-9.2 13.5-13.3 3.7-3.2 7.5-6.2 11.5-9 32.1-22.6 72.4-31.7 111.8-24.2C462 58.6 512 119.1 512 189.5v3.3c0 41.9-17.4 81.9-48.1 110.4L288.7 465.9l-2.5 2.3c-8.2 7.6-19 11.9-30.2 11.9s-22-4.2-30.2-11.9zM239.1 145c-.4-.3-.7-.7-1-1.1l-17.8-20-.1-.1c-23.1-25.9-58-37.7-92-31.2-46.6 8.9-80.2 49.5-80.2 96.9v3.3c0 28.5 11.9 55.8 32.8 75.2L256 430.7 431.2 268a102.7 102.7 0 0 0 32.8-75.2v-3.3c0-47.3-33.6-88-80.1-96.9-34-6.5-69 5.4-92 31.2l-.1.1-.1.1-17.8 20c-.3.4-.7.7-1 1.1-4.5 4.5-10.6 7-16.9 7s-12.4-2.5-16.9-7z" />
            </Svg>
          <Text className="text-white text-lg font-bold text-center">{post.score ?? 0}</Text>
        </View>
        {/* [/Number of likes] */}

        {/* [Number of comments] */}
        <View className="flex flex-col items-center justify-center space-x-2">
          <Svg
            xmlns="http://www.w3.org/2000/svg"
            height={32}
            width={32}
            viewBox="0 0 512 512"
            fill={"white"}
          >
            <Path d="M123.6 391.3c12.9-9.4 29.6-11.8 44.6-6.4 26.5 9.6 56.2 15.1 87.8 15.1 124.7 0 208-80.5 208-160S380.7 80 256 80 48 160.5 48 240c0 32 12.4 62.8 35.7 89.2 8.6 9.7 12.8 22.5 11.8 35.5-1.4 18.1-5.7 34.7-11.3 49.4 17-7.9 31.1-16.7 39.4-22.7zM21.2 431.9c1.8-2.7 3.5-5.4 5.1-8.1 10-16.6 19.5-38.4 21.4-62.9C17.7 326.8 0 285.1 0 240 0 125.1 114.6 32 256 32s256 93.1 256 208-114.6 208-256 208c-37.1 0-72.3-6.4-104.1-17.9-11.9 8.7-31.3 20.6-54.3 30.6-15.1 6.6-32.3 12.6-50.1 16.1-.8.2-1.6.3-2.4.5-4.4.8-8.7 1.5-13.2 1.9-.2 0-.5.1-.7.1-5.1.5-10.2.8-15.3.8-6.5 0-12.3-3.9-14.8-9.9S0 457.4 4.5 452.8c4.1-4.2 7.8-8.7 11.3-13.5 1.7-2.3 3.3-4.6 4.8-6.9.1-.2.2-.3.3-.5z" />
          </Svg>
          <Text className="text-white text-lg font-bold">{post.num_comments ?? 0}</Text>
        </View>
        {/* [/Number of comments] */}
        {/* [Number of bookmarks/awards] */}
        <View className="flex flex-col items-center justify-center space-x-2">
          <Svg
            xmlns="http://www.w3.org/2000/svg"
            height={32}
            width={32}
            viewBox="0 0 384 512"
            fill={'white'}
          >
            <Path d="M0 48C0 21.5 21.5 0 48 0v441.4l130.1-92.9c8.3-6 19.6-6 27.9 0l130 92.9V48H48V0h288c26.5 0 48 21.5 48 48v440c0 9-5 17.2-13 21.3s-17.6 3.4-24.9-1.8L192 397.5l-154.1 110c-7.3 5.2-16.9 5.9-24.9 1.8S0 497 0 488V48z" />
          </Svg>
          <Text className="text-white text-lg font-bold">{post.total_awards_received ?? 0}</Text>
        </View>
        {/* [/Number of bookmarks/awards] */}


        {/* [Share the post] */}
        <View className="flex flex-col items-center justify-center space-x-2">
          <Svg
            xmlns="http://www.w3.org/2000/svg"
            height={32}
            width={32}
            viewBox="0 0 512 512"
            fill={'white'}
          >
            <Path d="M307 34.8c-11.5 5.1-19 16.6-19 29.2v64H176C78.8 128 0 206.8 0 304c0 113.3 81.5 163.9 100.2 174.1 2.5 1.4 5.3 1.9 8.1 1.9 10.9 0 19.7-8.9 19.7-19.7 0-7.5-4.3-14.4-9.8-19.5-9.4-8.9-22.2-26.4-22.2-56.8 0-53 43-96 96-96h96v64c0 12.6 7.4 24.1 19 29.2s25 3 34.4-5.4l160-144c6.7-6.1 10.6-14.7 10.6-23.8s-3.8-17.7-10.6-23.8l-160-144a31.76 31.76 0 0 0-34.4-5.4z" />
          </Svg>
        </View>
        {/* [/Share the post] */}
      </View>
      {/* [/CTA buttons */}
    </View>
  );
};

... and here is author.tsx

import React from 'react';
import { View, Text, Image } from 'react-native';
import Svg, {Path} from 'react-native-svg';

const AuthorInfoComponent = ({ username, profilePic, createdAt }) => {
  return (
    <View className="flex flex-row justify-start items-center space-x-2">
      <View className="rounded-full bg-blue-600 p-2">
        <Svg
          xmlns="http://www.w3.org/2000/svg"
          width={24}
          height={24}
          viewBox="-4.771 0.104 53.521 44.858"
        >
          <Path
            fill="#FFF"
            d="M29.909 35.89c-1.999 1.997-5.218 2.382-7.921 2.382-2.7 0-5.922-.385-7.918-2.382M36.021 4.276 25.899 1.894l-3.93 11.996L25.9 1.894m18.241 3.201a3.99 3.99 0 1 1-7.98 0 3.991 3.991 0 0 1 7.98 0zm.661 23.906c0 8.262-10.263 14.961-22.922 14.961-12.66 0-22.922-6.698-22.922-14.961 0-8.262 10.262-14.961 22.922-14.961 12.659 0 22.922 6.698 22.922 14.961zM-.744 26.676a5.061 5.061 0 0 1-3.027-4.636 5.06 5.06 0 0 1 8.935-3.257m33.568.103a5.061 5.061 0 0 1 9.018 3.154 5.064 5.064 0 0 1-3.23 4.72"
          />
          <Path d="M21.879 44.963c-13.191 0-23.922-7.16-23.922-15.961 0-.608.051-1.21.151-1.801a6.066 6.066 0 0 1-2.879-5.161 6.068 6.068 0 0 1 6.06-6.061c1.493 0 2.916.546 4.017 1.522 4.149-2.663 9.73-4.339 15.887-4.455L25.235.71l.882.208.021.005 9.421 2.218A5 5 0 0 1 40.151.105a4.996 4.996 0 0 1 4.99 4.991 4.996 4.996 0 0 1-4.99 4.99 4.995 4.995 0 0 1-4.99-4.984l-8.596-2.024-3.273 9.99c5.933.231 11.291 1.912 15.291 4.517a6.028 6.028 0 0 1 4.108-1.605 6.068 6.068 0 0 1 6.061 6.061 6.019 6.019 0 0 1-3.08 5.28c.087.553.132 1.113.132 1.681-.002 8.801-10.734 15.961-23.925 15.961zM.157 27.11a9.05 9.05 0 0 0-.2 1.892c0 7.699 9.834 13.961 21.922 13.961 12.088 0 21.922-6.263 21.922-13.961 0-.612-.062-1.215-.183-1.807a1.003 1.003 0 0 1-.099-.435c-.669-2.627-2.494-5.012-5.13-6.934a.992.992 0 0 1-.429-.304c-4.007-2.755-9.732-4.482-16.081-4.482-6.285 0-11.961 1.693-15.962 4.401a1.022 1.022 0 0 1-.401.279C2.823 21.643.951 24.044.256 26.694a.992.992 0 0 1-.084.384c-.005.011-.009.022-.015.032zm40.097-8.319c2.319 1.855 4.021 4.064 4.891 6.488a4.033 4.033 0 0 0 1.605-3.239 4.065 4.065 0 0 0-4.061-4.061 4.04 4.04 0 0 0-2.435.812zm-38.965-.812a4.065 4.065 0 0 0-4.06 4.061c0 1.213.54 2.34 1.436 3.1.899-2.405 2.618-4.596 4.946-6.433a4.066 4.066 0 0 0-2.322-.728zM40.15 2.104c-1.648 0-2.99 1.342-2.99 2.991s1.342 2.99 2.99 2.99 2.99-1.341 2.99-2.99-1.341-2.991-2.99-2.991zM21.988 39.271c-4.005 0-6.827-.875-8.626-2.675a1 1 0 0 1 1.415-1.414c1.405 1.405 3.763 2.089 7.211 2.089 3.447 0 5.807-.684 7.214-2.089a.999.999 0 1 1 1.413 1.414c-1.801 1.8-4.622 2.675-8.627 2.675z" />
          <Path
            fill="#FF4500"
            d="M30.097 22.35c-2.038 0-3.749 1.707-3.749 3.745 0 2.037 1.711 3.688 3.749 3.688s3.688-1.651 3.688-3.688c0-2.038-1.651-3.745-3.688-3.745zm-16.158 0c-2.036 0-3.745 1.709-3.745 3.745s1.708 3.688 3.745 3.688 3.688-1.652 3.688-3.688-1.652-3.745-3.688-3.745z"
          />
        </Svg>
      </View>
      <View>
        <Text className="text-white text-lg">u/{username}</Text>
        <Text className="text-white">Created {createdAt.toLocaleString()}</Text>
      </View>
    </View>
  );
};

export default AuthorInfoComponent;

Showcase

Here is a picture of the ultimate slop machine: RedTok

... it has some minor inconsistencies, but I am suffering from TikTok withdrawl symptome, so I decided to ship first and test later #code.

And here is a video of the app running on my iPhone.

Conclusion: Unleash Your Inner TikToker, Embrace the Chaos! 🎉

And there you have it, folks! We've embarked on an epic journey to summon from the slumbers the boomer's vision of hell. Remember, dear readers, in the realm of app development, nothing is impossible, and laughter is the best medicine. So go forth, build something extraordinary, and may your code be as witty and brilliant as the content you fetch from Reddit, hoping that this post was not too long for everyone's attention span.

If you want to check out the source code, here is the GitHub repo.

Happy coding 👋

info@niturobert.com

Get in touch