Skip to content

Commit

Permalink
Add follows
Browse files Browse the repository at this point in the history
  • Loading branch information
avvazana committed Dec 31, 2018
1 parent 65a7fa8 commit 7d55018
Show file tree
Hide file tree
Showing 28 changed files with 207 additions and 28 deletions.
13 changes: 13 additions & 0 deletions app/assets/stylesheets/components/show_box_content.scss
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,19 @@
font-family: Circular,Helvetica,Arial,sans-serif;
}

.show-follow {
display: flex;
justify-content: center;
}

.follow-button {
padding-top: 10px;
color: lightgray;
font-weight: 100;
font-size: 14px;
font-family: Circular,Helvetica,Arial,sans-serif;
}

.loading {
width: 60px;
margin: auto;
Expand Down
31 changes: 31 additions & 0 deletions app/controllers/api/follows_controller.rb
Original file line number Diff line number Diff line change
@@ -1,2 +1,33 @@
class Api::FollowsController < ApplicationController
def create

@follow = Follow.new(follow_params)
if @follow.save
render 'api/follows/show'
else
render json: ["Could not process request"], status: 401
end
end

def destroy

@follow = Follow.find_by(
user_id: follow_params[:user_id],
followable_id: follow_params[:followable_id],
followable_type: follow_params[:followable_type],
)
if @follow
f = @follow.dup
@follow.destroy
render json: f
else
render json: ["Connection could not be found"], status: 404
end
end

private

def follow_params
params.require(:follow).permit(:user_id, :followable_id, :followable_type)
end
end
3 changes: 3 additions & 0 deletions app/models/artist.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,7 @@ class Artist < ApplicationRecord
has_many :albums,
through: :songs,
source: :album

has_many :follows, as: :followable, dependent: :destroy
has_many :followers, through: :follows, source: :user
end
7 changes: 6 additions & 1 deletion app/models/follow.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,13 @@
# followeable_type :string
# created_at :datetime not null
# updated_at :datetime not null
# user_id :integer
#

class Follow < ApplicationRecord
belongs_to :followeable_id
validates :user_id, :followable_id, :followable_type, presence: true
validates :user_id, uniqueness: { :scope => [:followable_type, :followable_id] }

belongs_to :user
belongs_to :followable, polymorphic: true
end
3 changes: 3 additions & 0 deletions app/models/playlist.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,7 @@ class Playlist < ApplicationRecord

has_one_attached :photo
has_one_attached :track

has_many :follows, as: :followable, dependent: :destroy
has_many :followers, through: :follows, source: :user
end
4 changes: 4 additions & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ class User < ApplicationRecord
through: :songs,
source: :artists

has_many :follows
has_many :followed_artists, through: :follows, source: :followable, source_type: 'Artist'
has_many :followed_playlists, through: :follows, source: :followable, source_type: 'Playlist'

def password=(password)
@password = password
self.password_digest = BCrypt::Password.create(password)
Expand Down
1 change: 1 addition & 0 deletions app/views/api/follows/show.json.jbuilder
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
json.extract! @follow, :id, :user_id, :followable_id, :followable_type
2 changes: 1 addition & 1 deletion app/views/api/users/_user.json.jbuilder
Original file line number Diff line number Diff line change
@@ -1 +1 @@
json.extract! user, :id, :username
json.extract! user, :id, :username, :followed_artist_ids, :followed_playlist_ids
2 changes: 1 addition & 1 deletion app/views/api/users/show.json.jbuilder
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@

json.extract! @user, :id, :username, :song_ids, :album_ids
json.extract! @user, :id, :username, :song_ids, :album_ids, :followed_artist_ids, :followed_playlist_ids
# json.artist_ids @artists.pluck(:id)
5 changes: 5 additions & 0 deletions db/migrate/20181229235133_add_userid_to_follows.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddUseridToFollows < ActiveRecord::Migration[5.2]
def change
add_column :follows, :user_id, :integer
end
end
6 changes: 6 additions & 0 deletions db/migrate/20181229235944_change_follows_column_names.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class ChangeFollowsColumnNames < ActiveRecord::Migration[5.2]
def change
rename_column :follows, :followeable_id, :followable_id
rename_column :follows, :followeable_type, :followable_type
end
end
7 changes: 4 additions & 3 deletions db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 2018_11_18_235623) do
ActiveRecord::Schema.define(version: 2018_12_29_235944) do

# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
Expand Down Expand Up @@ -50,10 +50,11 @@
end

create_table "follows", force: :cascade do |t|
t.integer "followeable_id"
t.string "followeable_type"
t.integer "followable_id"
t.string "followable_type"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
end

create_table "playlists", force: :cascade do |t|
Expand Down
30 changes: 30 additions & 0 deletions frontend/actions/follow_actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import * as FollowAPIUtil from '../util/follow_api_util';

export const RECEIVE_FOLLOW = 'RECEIVE_FOLLOW';
export const REMOVE_FOLLOW = 'REMOVE_FOLLOW';

const receiveFollow = follow => {
return {
type: RECEIVE_FOLLOW,
follow
};
};

const removeFollow = follow => {
return {
type: REMOVE_FOLLOW,
follow
};
};

export const createFollow = follow => {
return dispatch => FollowAPIUtil.createFollow(follow).then(
res => dispatch(receiveFollow(res))
);
};

export const deleteFollow = follow => {
return dispatch => FollowAPIUtil.deleteFollow(follow).then(
res => dispatch(removeFollow(res))
);
};
2 changes: 1 addition & 1 deletion frontend/actions/search_actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export const RECEIVE_ALL_ALBUMS = 'RECEIVE_ALL_ALBUMS';
export const RECEIVE_ALBUM = 'RECEIVE_ALBUM';

export const requestAllAlbums = (searchQuery) => {
debugger

return (dispatch) => {
return SearchApiUtil.fetchAllAlbums(searchQuery)
.then(null,
Expand Down
2 changes: 1 addition & 1 deletion frontend/components/main/header/album_index_container.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { requestAllAlbums } from '../../../actions/search_actions';
import { selectRandomAlbums } from '../../../reducers/selectors';

const mapStateToProps = (state, ownProps) => {
debugger

return {
path: "album",
navpath: ownProps.navpath,
Expand Down
13 changes: 10 additions & 3 deletions frontend/components/main/header/artist_show_container.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,31 @@ import { connect } from 'react-redux';
import GridShow from './grid_show';
import { logout } from '../../../actions/session_actions';
import { selectArtistSongs } from '../../../reducers/selectors';
import { createFollow, deleteFollow } from '../../../actions/follow_actions';


const mapStateToProps = (state, ownProps) => {
debugger

// const artist = Object.values(state.entities.remoteArtists)[ownProps.match.params.artistId] || ownProps.artists || [],
const artist = state.entities.artists[ownProps.match.params.artistId] || { name: "", song_ids: [], photoUrl: "" };
const songs = selectArtistSongs(state, artist);

// const currentUser = state.entities.users[state.session.currentUserId];
const currentUser = state.session.currentUser;
return {
artist,
artistId: ownProps.match.params.artistId,
songs
songs,
currentUser
};
};

const mapDipatchToProps = (dispatch) => {
return {
fetchArtist: (id) => dispatch(fetchArtist(id)),
logout: () => dispatch(logout())
logout: () => dispatch(logout()),
createFollow: (follow) => dispatch(createFollow(follow)),
deleteFollow: (follow) => dispatch(deleteFollow(follow))
};
};

Expand Down
4 changes: 2 additions & 2 deletions frontend/components/main/header/grid_index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class GridIndex extends React.Component {
}

componentDidMount(){
debugger

this.fetchElements(
{search_term: this.props.searchTerm}
);
Expand All @@ -26,7 +26,7 @@ class GridIndex extends React.Component {
}

render(){
debugger

const {playlists, artists, albums, navpath, path} = this.props;
let property = "title";
let gridElements = [];
Expand Down
27 changes: 27 additions & 0 deletions frontend/components/main/header/grid_show.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ class GridShow extends React.Component {
this.elementId = this.props.playlistId || this.props.albumId || this.props.artistId;
this.fetchElement = this.fetchElement.bind(this);
this.timeout = this.timeout.bind(this);
this.handleFollow = this.handleFollow.bind(this);

}

timeout(){
Expand All @@ -25,6 +27,18 @@ class GridShow extends React.Component {
.then( this.timeout );
}

handleFollow(e) {

const following = this.props.currentUser.followed_artist_ids.includes(this.props.artist.id);
const follow = {
user_id: this.props.currentUser.id,
followable_id: this.props.artist.id,
followable_type: 'Artist'
};

following ? this.props.deleteFollow(follow) : this.props.createFollow(follow);
}

render () {

function isDefined(song){
Expand Down Expand Up @@ -66,6 +80,16 @@ class GridShow extends React.Component {
element.artworkUrl100 = element.artworkUrl100.replace('100x100', '600x600');
}

let follows = "";
if (this.props.artist) {
follows = (
<button className="follow-button"
onClick={this.handleFollow}>
{this.props.currentUser.followed_artist_ids.includes(this.props.artist.id) ? 'Unfollow' : 'Follow'}
</button>
);
}

return (
<div className="main-container">
<div className="show-body">
Expand All @@ -78,6 +102,9 @@ class GridShow extends React.Component {
<p>{element.title || element.name || element.collectionName}</p>
<span>{element.author || element.artists || element.artistName}</span>
</div>
<div className="show-follow">
{follows}
</div>
</div>
<div className="tracks">
{tracks}
Expand Down
2 changes: 1 addition & 1 deletion frontend/components/main/header/search/search_results.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class SearchResults extends React.Component {
// </li>
// </ul>
// </div>
debugger

return (
<div className="search-results">
<div className="search-section">
Expand Down
2 changes: 1 addition & 1 deletion frontend/components/main/header/songs_index_item.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class SongsIndexItem extends React.Component {
this.props.pauseCurrentSong();
this.setState({ playing: false });
} else {
debugger

this.props.receiveCurrentSong(songId, elementId, elementType);
this.setState({ playing: true });
}
Expand Down
1 change: 1 addition & 0 deletions frontend/components/main/navbar/browse_container.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { selectAllUnauthoredPlaylists, selectRandomAlbums, selectRandomArtists}
import MainContent from '../main_content';

const mapStateToProps = (state, ownProps) => {

return {
playlists: selectAllUnauthoredPlaylists(state),
albums: selectRandomAlbums(state),
Expand Down
2 changes: 1 addition & 1 deletion frontend/components/main/playbar/music_player.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ class MusicPlayer extends React.Component {
}

nextSong(currentSongId) {
debugger

let songList = this.props.songList;
songList = songList.map((el) => {
return parseInt(el);
Expand Down
3 changes: 2 additions & 1 deletion frontend/reducers/entities/entities_reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ export default combineReducers({
artists: artistsReducer,
albums: albumsReducer,
remoteAlbums: searchAlbumsReducer,
remoteSongs: searchSongsReducer
remoteSongs: searchSongsReducer,
users: usersReducer
});
13 changes: 8 additions & 5 deletions frontend/reducers/entities/users_reducer.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { RECEIVE_CURRENT_USER, RECEIVE_USER} from '../../actions/session_actions';
import { RECEIVE_FOLLOW, REMOVE_FOLLOW } from '../../actions/follow_actions';
import { merge } from 'lodash';

const usersReducer = (state = {}, action) => {
Object.freeze(state);

switch(action.type) {
const usersReducer = ( state = {}, action ) => {

Object.freeze(state);
switch (action.type) {
case RECEIVE_CURRENT_USER:
const currentUser = {[action.user.id]: action.user};
return Object.assign({}, state, currentUser);
const currentUser = action.currentUser;
return merge({}, state, { currentUser });
default:
return state;
}
Expand Down
2 changes: 1 addition & 1 deletion frontend/reducers/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export const selectAlbumSongs = (state, album) => {
export const selectAllSongs = state => Object.values(state.entities.songs);

export const getSongList = (state, currentSong) => {
debugger

if (!currentSong.id) {
return [];
}
Expand Down
Loading

0 comments on commit 7d55018

Please sign in to comment.