Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 60 additions & 61 deletions platforms/blabsy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,113 +12,112 @@ https://user-images.githubusercontent.com/55032197/201472767-9db0177a-79b5-4913-

## Features ✨

- Authentication with Firebase Authentication
- Strongly typed React components with TypeScript
- Users can add tweets, like, retweet, and reply
- Users can delete tweets, add a tweet to bookmarks, and pin their tweet
- Users can add images and GIFs to tweet
- Users can follow and unfollow other users
- Users can see their and other followers and the following list
- Users can see all users and the trending list
- Realtime update likes, retweets, and user profile
- Realtime trending data from Twitter API
- User can edit their profile
- Responsive design for mobile, tablet, and desktop
- Users can customize the site color scheme and color background
- All images uploads are stored on Firebase Cloud Storage
- Authentication with Firebase Authentication
- Strongly typed React components with TypeScript
- Users can add blabs, like, reblab, and reply
- Users can delete blabs, add a blab to bookmarks, and pin their blab
- Users can add images and GIFs to blab
- Users can follow and unfollow other users
- Users can see their and other followers and the following list
- Users can see all users and the trending list
- Realtime update likes, reblabs, and user profile
- User can edit their profile
- Responsive design for mobile, tablet, and desktop
- Users can customize the site color scheme and color background
- All images uploads are stored on Firebase Cloud Storage

## Tech 🛠

- [Next.js](https://nextjs.org)
- [TypeScript](https://www.typescriptlang.org)
- [Tailwind CSS](https://tailwindcss.com)
- [Firebase](https://firebase.google.com)
- [SWR](https://swr.vercel.app)
- [Headless UI](https://headlessui.com)
- [React Hot Toast](https://react-hot-toast.com)
- [Framer Motion](https://framer.com)
- [Next.js](https://nextjs.org)
- [TypeScript](https://www.typescriptlang.org)
- [Tailwind CSS](https://tailwindcss.com)
- [Firebase](https://firebase.google.com)
- [SWR](https://swr.vercel.app)
- [Headless UI](https://headlessui.com)
- [React Hot Toast](https://react-hot-toast.com)
- [Framer Motion](https://framer.com)

## Development 💻

Here are the steps to run the project locally.

1. Clone the repository

```bash
git clone https://github.com/ccrsxx/twitter-clone.git
```
```bash
git clone https://github.com/MetaState-Prototype-Project/prototype.git
```

1. Install dependencies

```bash
npm i
```
```bash
npm i
```

1. Create a Firebase project and select the web app

1. Add your Firebase config to `.env.development`. Note that `NEXT_PUBLIC_MEASUREMENT_ID` is optional

1. Make sure you have enabled the following Firebase services:

- Authentication. Enable the Google sign-in method.
- Cloud Firestore. Create a database and set its location to your nearest region.
- Cloud Storage. Create a storage bucket.
- Authentication. Enable the Google sign-in method.
- Cloud Firestore. Create a database and set its location to your nearest region.
- Cloud Storage. Create a storage bucket.

1. Install Firebase CLI globally

```bash
npm i -g firebase-tools
```
```bash
npm i -g firebase-tools
```

1. Log in to Firebase

```bash
firebase login
```
```bash
firebase login
```

1. Get your project ID

```bash
firebase projects:list
```
```bash
firebase projects:list
```

1. Select your project ID

```bash
firebase use your-project-id
```
```bash
firebase use your-project-id
```

1. At this point, you have two choices. Either run this project using the Firebase on the cloud or locally using emulator.

1. Using the Firebase Cloud Backend:
1. Using the Firebase Cloud Backend:

1. Deploy Firestore rules, Firestore indexes, and Cloud Storage rules
1. Deploy Firestore rules, Firestore indexes, and Cloud Storage rules

```bash
firebase deploy --except functions
```
```bash
firebase deploy --except functions
```

1. Run the project
1. Run the project

```bash
npm run dev
```
```bash
npm run dev
```

1. Using Firebase Local Emulator:
1. Using Firebase Local Emulator:

1. Install [Java JDK version 11 or higher](https://jdk.java.net/) before proceeding. This is required to run the emulators.
1. Install [Java JDK version 11 or higher](https://jdk.java.net/) before proceeding. This is required to run the emulators.

1. Set the environment variable `NEXT_PUBLIC_USE_EMULATOR` to `true` in `.env.development`. This will make the app use the emulators instead of the cloud backend.
1. Set the environment variable `NEXT_PUBLIC_USE_EMULATOR` to `true` in `.env.development`. This will make the app use the emulators instead of the cloud backend.

1. At this point, you can run the following command to have a fully functional Twitter clone running locally:
1. At this point, you can run the following command to have a fully functional Twitter clone running locally:

```bash
npm run dev:emulators
```
```bash
npm run dev:emulators
```

> **_Note_**: When you deploy Firestore indexes rules, it might take a few minutes to complete. So before the indexes are enabled, you will get an error when you fetch the data from Firestore.<br><br>You can check the status of your Firestore indexes with the link below, replace `your-project-id` with your project ID: https://console.firebase.google.com/u/0/project/your-project-id/firestore/indexes

Optional:

- If you want to get trending data from Twitter API, you need to create a Twitter developer account and get your API keys. Then add your API keys to `.env.development`. I hope Elon Musk doesn't make this API paid 😅.
- If you want to make the user stats synced with the deleted tweets, you need to enable the Cloud Functions for Firebase. Then deploy the Cloud Functions.
- If you want to get trending data from Twitter API, you need to create a Twitter developer account and get your API keys. Then add your API keys to `.env.development`. I hope Elon Musk doesn't make this API paid 😅.
- If you want to make the user stats synced with the deleted blabs, you need to enable the Cloud Functions for Firebase. Then deploy the Cloud Functions.
68 changes: 34 additions & 34 deletions platforms/blabsy/functions/src/normalize-stats.ts
Original file line number Diff line number Diff line change
@@ -1,49 +1,49 @@
import { functions, firestore, regionalFunctions } from './lib/utils';
import { tweetConverter, bookmarkConverter } from './types';
import type { Tweet } from './types';
import { firestore, functions, regionalFunctions } from "./lib/utils";
import { bookmarkConverter, tweetConverter } from "./types";
import type { Tweet } from "./types";

export const normalizeStats = regionalFunctions.firestore
.document('tweets/{tweetId}')
.onDelete(async (snapshot): Promise<void> => {
const tweetId = snapshot.id;
const tweetData = snapshot.data() as Tweet;
.document("tweets/{tweetId}")
.onDelete(async (snapshot): Promise<void> => {
const tweetId = snapshot.id;
const tweetData = snapshot.data() as Tweet;

functions.logger.info(`Normalizing stats from tweet ${tweetId}`);
functions.logger.info(`Normalizing stats from tweet ${tweetId}`);

const { userRetweets, userLikes } = tweetData;
const { userRetweets, userLikes } = tweetData;

const usersStatsToDelete = new Set([...userRetweets, ...userLikes]);
const usersStatsToDelete = new Set([...userRetweets, ...userLikes]);

const batch = firestore().batch();
const batch = firestore().batch();

usersStatsToDelete.forEach((userId) => {
functions.logger.info(`Deleting stats from ${userId}`);
usersStatsToDelete.forEach((userId) => {
functions.logger.info(`Deleting stats from ${userId}`);

const userStatsRef = firestore()
.doc(`users/${userId}/stats/stats`)
.withConverter(tweetConverter);
const userStatsRef = firestore()
.doc(`users/${userId}/stats/stats`)
.withConverter(tweetConverter);

batch.update(userStatsRef, {
tweets: firestore.FieldValue.arrayRemove(tweetId),
likes: firestore.FieldValue.arrayRemove(tweetId)
});
});
batch.update(userStatsRef, {
tweets: firestore.FieldValue.arrayRemove(tweetId),
likes: firestore.FieldValue.arrayRemove(tweetId),
});
});

const bookmarksQuery = firestore()
.collectionGroup('bookmarks')
.where('id', '==', tweetId)
.withConverter(bookmarkConverter);
const bookmarksQuery = firestore()
.collectionGroup("bookmarks")
.where("id", "==", tweetId)
.withConverter(bookmarkConverter);

const docsSnap = await bookmarksQuery.get();
const docsSnap = await bookmarksQuery.get();

functions.logger.info(`Deleting ${docsSnap.size} bookmarks`);
functions.logger.info(`Deleting ${docsSnap.size} bookmarks`);

docsSnap.docs.forEach(({ id, ref }) => {
functions.logger.info(`Deleting bookmark ${id}`);
batch.delete(ref);
});
docsSnap.docs.forEach(({ id, ref }) => {
functions.logger.info(`Deleting bookmark ${id}`);
batch.delete(ref);
});

await batch.commit();
await batch.commit();

functions.logger.info(`Normalizing stats for tweet ${tweetId} is done`);
});
functions.logger.info(`Normalizing stats for blab ${tweetId} is done`);
});
86 changes: 44 additions & 42 deletions platforms/blabsy/functions/src/notify-email.ts
Original file line number Diff line number Diff line change
@@ -1,45 +1,47 @@
import { createTransport } from 'nodemailer';
import { firestore, functions, regionalFunctions } from './lib/utils';
import { EMAIL_API, EMAIL_API_PASSWORD, TARGET_EMAIL } from './lib/env';
import type { Tweet, User } from './types';
import { createTransport } from "nodemailer";
import { EMAIL_API, EMAIL_API_PASSWORD, TARGET_EMAIL } from "./lib/env";
import { firestore, functions, regionalFunctions } from "./lib/utils";
import type { Tweet, User } from "./types";

export const notifyEmail = regionalFunctions.firestore
.document('tweets/{tweetId}')
.onCreate(async (snapshot): Promise<void> => {
functions.logger.info('Sending notification email.');

const { text, createdBy, images, parent } = snapshot.data() as Tweet;

const imagesLength = images?.length ?? 0;

const { name, username } = (
await firestore().doc(`users/${createdBy}`).get()
).data() as User;

const client = createTransport({
service: 'Gmail',
auth: {
user: EMAIL_API.value(),
pass: EMAIL_API_PASSWORD.value()
}
});

const tweetLink = `https://twitter-clone-ccrsxx.vercel.app/tweet/${snapshot.id}`;

const emailHeader = `New Tweet${
parent ? ' reply' : ''
} from ${name} (@${username})`;

const emailText = `${text ?? 'No text provided'}${
images ? ` (${imagesLength} image${imagesLength > 1 ? 's' : ''})` : ''
}\n\nLink to Tweet: ${tweetLink}\n\n- Firebase Function.`;

await client.sendMail({
from: EMAIL_API.value(),
to: TARGET_EMAIL.value(),
subject: emailHeader,
text: emailText
.document("tweets/{tweetId}")
.onCreate(async (snapshot): Promise<void> => {
functions.logger.info("Sending notification email.");

const { text, createdBy, images, parent } = snapshot.data() as Tweet;

const imagesLength = images?.length ?? 0;

const { name, username } = (
await firestore().doc(`users/${createdBy}`).get()
).data() as User;

const client = createTransport({
service: "Gmail",
auth: {
user: EMAIL_API.value(),
pass: EMAIL_API_PASSWORD.value(),
},
});

const tweetLink = `https://twitter-clone-ccrsxx.vercel.app/tweet/${snapshot.id}`;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify URL domain consistency with Blabsy rebranding.

The email contains a link to twitter-clone-ccrsxx.vercel.app which may be inconsistent with the Blabsy rebranding effort. Consider updating the domain to reflect the new branding if applicable.


🏁 Script executed:

#!/bin/bash
# Description: Check if this domain URL appears elsewhere in the codebase and verify if it should be updated
# Expected: Find other references to determine if domain should be updated for consistency

# Search for the specific domain
rg "twitter-clone-ccrsxx.vercel.app" --type ts --type tsx --type js --type jsx

# Search for any environment variables or config files that might define the base URL
rg "NEXT_PUBLIC_BASE_URL|BASE_URL|DOMAIN" --type env --type ts --type js

Length of output: 209


I’ll broaden the search to catch all occurrences and any config references:


🏁 Script executed:

#!/usr/bin/env bash
# 1. Search for the hard-coded domain anywhere in the repo
rg "twitter-clone-ccrsxx.vercel.app"

# 2. Look for environment/config variables that could define the base URL or domain
rg -i "BASE_URL|NEXT_PUBLIC_BASE_URL|DOMAIN" -g '*.env' -g '*.json' -g '*.ts' -g '*.js'

Length of output: 1682


Use a configurable Blabsy base URL instead of hard-coding the old domain

It looks like notify-email.ts is still generating links with twitter-clone-ccrsxx.vercel.app, and there isn’t a matching environment variable in this function. To keep branding consistent and make future updates painless:

  • Define (or reuse) an env var, e.g. PUBLIC_BLABSY_BASE_URL, for the Blabsy site URL.

  • Update platforms/blabsy/functions/src/notify-email.ts to use that var with a sensible default:

    // platforms/blabsy/functions/src/notify-email.ts
           // before
  •    const tweetLink = `https://twitter-clone-ccrsxx.vercel.app/tweet/${snapshot.id}`;
    
       // after
    
  •    const baseUrl =
    
  •      process.env.PUBLIC_BLABSY_BASE_URL ||
    
  •      'https://twitter-clone-ccrsxx.vercel.app';
    
  •    const tweetLink = `${baseUrl}/tweet/${snapshot.id}`;
    
    
    

This ensures all outbound links adopt the correct Blabsy domain and can be updated centrally.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const tweetLink = `https://twitter-clone-ccrsxx.vercel.app/tweet/${snapshot.id}`;
// platforms/blabsy/functions/src/notify-email.ts
// replace hard-coded tweetLink with a configurable Blabsy base URL
const baseUrl =
process.env.PUBLIC_BLABSY_BASE_URL ||
'https://twitter-clone-ccrsxx.vercel.app';
const tweetLink = `${baseUrl}/tweet/${snapshot.id}`;
🤖 Prompt for AI Agents
In platforms/blabsy/functions/src/notify-email.ts at line 27, the tweet link is
hard-coded with the old domain twitter-clone-ccrsxx.vercel.app, which is
inconsistent with the Blabsy rebranding. To fix this, introduce an environment
variable like PUBLIC_BLABSY_BASE_URL to hold the base URL for Blabsy, provide a
sensible default if the env var is not set, and update the tweetLink
construction to use this variable instead of the hard-coded domain. This change
centralizes domain configuration and ensures branding consistency.


const emailHeader = `New Blab${
parent ? " reply" : ""
} from ${name} (@${username})`;

const emailText = `${text ?? "No text provided"}${
images
? ` (${imagesLength} image${imagesLength > 1 ? "s" : ""})`
: ""
}\n\nLink to Blab: ${tweetLink}\n\n- Firebase Function.`;

await client.sendMail({
from: EMAIL_API.value(),
to: TARGET_EMAIL.value(),
subject: emailHeader,
text: emailText,
});

functions.logger.info("Notification email sent.");
});

functions.logger.info('Notification email sent.');
});

Large diffs are not rendered by default.

Loading
Loading