/synthagram

Photo sharing for synth nerds

Primary LanguageJavaScript

Synthagram

Live Link

Synthagram is a music-themed clone of Instagram built with the MERN stack + GraphQL/Apollo. The design is simple, clean, and mobile-first, resulting in an app that is responsive across all screen sizes.

Created by Alex Lee and Eli Raybon over 3 days.

Technologies

  • React
  • Node
  • MongoDB
  • Express
  • GraphQL
  • Apollo
  • Dropzone
  • AWS S3
  • Multer (for uploading to S3)
  • BCrypt (for user auth)
  • HTML
  • CSS

Features

  • Drag n' drop file uploads
  • Photo upload previews
  • Double tap photo likes
  • Comments and nested replies
  • Following and unfollowing users
  • Search Bar
  • User Authentication

The photos above give you a quick look at the main feed of Synthagram. Your feed is populated by the most recent photos of the users that you follow. You can double-tap a photo to like it, which triggers an animation.

Photos in the feed are sorted using a recurive quicksort algorithm.

sortByDate = photos => {
  if (photos.length < 2) return photos;

  const pivotPhoto = photos[0];
  let older = photos.slice(1).filter(photo => photo.created > pivotPhoto.created);
  let newer = photos.slice(1).filter(photo => photo.created <= pivotPhoto.created);
  older = sortByDate(older);
  newer = sortByDate(newer);

  return older.concat([pivotPhoto]).concat(newer);
} 

From your feed, you quickly jump to the profile of a particular user, where can can browse all of their synthtastic snaps.

If you don't have enough synths to look at, you can head over to the explore page using the compass icon in the navbar. There, you will be served an index of users you might want to follow as well as more musical madness.

You can post your own pictures using the drag and drop file uplaoder (desktop) or by tapping the upload box (mobile). On mobile, the uploader gives you easy access to your camera roll. Once a photo is chosen, you're given a preview to make sure everything looks good before posting!

Along with liking photos, you can also leave comments on them, as well as reply to comments.

The biggest backend challenge we ran into was deleting comments. Sounds simple right? But a comment can't simply be deleted from the database or it would leave behind several ghost references in its corresponding user and photo documents. Those are easy enough to clean up, so what's the big deal? Replies! When a comment is deleted, all of its replies also need to be deleted, and all of the replies of the replies. On and on and on. Sounds like a recursion problem!

To acheive this, we needed a way to loop over a root comment's replies one by one, recurively deleting the reply as well as all of it's nested replies. But comment deletion is an asynchronous process, so we started by writing a new verion of the forEach method that utilizes async/await.

async function asyncForEach(array, callback) {
  for (let index = 0; index < array.length; index++) {
    await callback(array[index], index, array);
  }
};

We also wrote a helper method called removeSingleComment that takes care of deleting a comment and removing all references to it from the database.

The final result was this removeNestedReplies function. It takes in a root comment and recursively deletes the comment and all of it's nested replies.

async function removeNestedReplies(rootComment) {
  const Comment = mongoose.model('comments');

  if (!rootComment.replies.length) {
    return removeSingleComment(rootComment)
  }

  asyncForEach(rootComment.replies, async (commentId) => {
    const comment = await Comment.findById(commentId);
    await removeNestedReplies(comment);
  })

  return removeSingleComment(rootComment);
};