Skip to content

Motify, a fullstack Spotify clone built with React, Redux, and Rails.

Notifications You must be signed in to change notification settings

garysbot/motify

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Motify: A Fullstack Spotify Clone

Motify is a full-stack clone of Spotify designed to emulate the core functionalities of the original platform with a responsive and intuitive user interface.

Motify Overview

Tech Stack

The Motify application employs a modern full-stack architecture:

  • React.js

React

- Ruby on Rails with PostgreSQL

Rails

PostgreSQL

- Redux

Redux

- Node.js

Nodejs

- HTML, CSS, Vanilla JavaScript

html5

css3

javascript

Data Architecture

motify-data-structure.png

The front end is built with React, leveraging HTML, CSS, and JavaScript to deliver a responsive user interface and user experience. State management within the front end is handled by Redux, which serves as a predictable state container, interfacing with middleware for asynchronous events.

Routing is managed through a combination of React for component rendering and Node.js for server-side logic, facilitating the navigation within the application.

The backend is powered by Ruby on Rails, providing a robust server-side framework. Data persistence is achieved with PostgreSQL, a powerful object-relational database system. The data serialization is handled by jBuilder, allowing for the creation of JSON structures that are consumed by the front end.

This architecture supports a scalable, maintainable, and interactive web application, designed for optimal user engagement and straightforward data flow from the database to the end user.

Core Features

  • Play Bar: Interactive play bar for music playback and controls.

  • Playlist Management: Comprehensive playlist management, including CRUD operations, song addition/removal, and custom playlist titles.

  • Hosting: Application deployment on Render.

html5


  • Dynamic Search: Real-time search across artists, songs, albums, and playlists. Dynamic Search

  • User Authentication: Secure login, registration, and session management with demo account access. Motify Signup Flow Motify Signup Flow

UI/UX Design Philosophy

Spotify's design philosophy revolves around ;

Highlighted Features

Dynamic Search

Dynamic real-time search fetching from Rails backend for Songs, Albums, Artists, and Playlists that match the search query.

Dynamic Search

import { useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import searchIcon from '../../static/icons/search-inactive.svg';
import closeIcon from '../../static/icons/close.svg';
import { performSearch, clearSearch } from '../../store/search';
import SearchResultsDropdown from './SearchResultsDropdown';

const SearchField = () => {
  const [inputValue, setInputValue] = useState('');
  const dispatch = useDispatch();
  const { error, results, searchInitiated, query } = useSelector(state => state.search);

  const handleChange = (event) => {
    const newQuery = event.target.value;
    setInputValue(newQuery);
    if (newQuery) {
      dispatch(performSearch(newQuery)); // Dispatching performSearch action with new query
    } else {
      dispatch(clearSearch()); // Clearing the search when input is empty
    }
  };

  const clearInput = () => {
    setInputValue('');
    dispatch(clearSearch()); // Dispatch Redux action to clear search
  };

  return (
    <>
      <div className='search-input-container'>
        <img src={searchIcon} alt='Search' className="search-icon"/>
        <input
          type='text'
          placeholder='Search for songs or episodes'
          value={inputValue}
          onChange={handleChange}
        />
        <img src={closeIcon} alt='Close' className='close-icon' onClick={clearInput}/>
      </div>
      {error && <p>Error: {error.message}</p>}
      <SearchResultsDropdown query={query} searchResults={results} searchInitiated={searchInitiated}/>
    </>
  );
}

export default SearchField;

SongsTable UI/UX with Redux State Management

Redux state management for globalized state enhancing user experience and user interface design. Song row style changes based on current song playing state.

Song State Redux

import { useSelector, useDispatch } from 'react-redux';
import { useState } from 'react';
import { receiveSong, togglePlay } from '../../store/audioActions'; // Import relevant actions
import { ReactSVG } from 'react-svg';
import lilPlayButton from '../../static/icons/noun-play-1009801.svg'

const SongsTable = () => {
  const dispatch = useDispatch();
  const currentAlbum = useSelector(state => state.audio.currentAlbum);
  const [hoveredTrack, setHoveredTrack] = useState(null);
  // Function to handle play button click
  const handlePlaySong = (song) => {
    dispatch(receiveSong(song));
    dispatch(togglePlay());
  };
  return (
    <div className='show-songs-table'>
      {
        currentAlbum.songs?.map((song, trackNum) =>
          <>
            <div
              className='show-songs-row-container'
              onMouseEnter={() => setHoveredTrack(trackNum)}
              onMouseLeave={() => setHoveredTrack(null)}
              onClick={() => handlePlaySong(song)} // ! This is what changes the Redux State
            >
              <div className='row-start'
              >
                <div className='track-num'>
                  {hoveredTrack === trackNum 
                    ?
                    (<ReactSVG src={lilPlayButton} className='anim-play-button' />)
                    :
                    (<p style={{'width':'12px', 'height':'12px'}}>{trackNum + 1}</p>)
                  }
                </div>
                <div className='song-title-artist-container'>
                  <p className='song-title'>{song.title}</p>
                  <p className='song-title-artist-name'>{currentAlbum.artistName}</p>
                </div>
                <div className='song-title-artist-container'>
                  <p className='song-title'>{song.album?.title}</p>
                </div>
              </div>

              <div className='row-end'>
                <div className='like-button-duration'>
                  <p className='duration-text header-time'>{`${Math.floor(song.duration / 60)}:${String(song.duration % 60).padStart(2, '0')}`}</p>
                </div>
              </div>

            </div>
          </>
        )
      }
    </div>
  )
}

export default SongsTable

Future Features

  • Enhanced user and artist profiles.
  • Social features to follow and unfollow users and artists.
  • Like/unlike functionality for songs, albums, and playlists.
  • Dynamic playlist cover generation based on song selection.

About

Motify, a fullstack Spotify clone built with React, Redux, and Rails.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published