-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
318 additions
and
280 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
<template name="searchBar"> | ||
<div class="row" id="searchArea"> | ||
{{#if param 'lovedToggle'}} | ||
<div class="col-md-{{param 'lovedToggleWidth'}}"> | ||
<div class="dropdown"> | ||
<button class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown"> | ||
<span class="glyphicon glyphicon-heart"></span><span class="caret"></span> | ||
</button> | ||
<ul class="dropdown-menu" role="menu"> | ||
{{#each lovedSongs}} | ||
<li><a class="loved" href="#">{{this.snippet.title}}</a></li> | ||
{{/each}} | ||
</ul> | ||
</div> | ||
</div> | ||
{{/if}} | ||
<div class="col-md-{{param 'searchBarWidth'}}"> | ||
<form class="youtube-search"> | ||
<div class="input-group"> | ||
<span class="input-group-addon"><span class="glyphicon glyphicon-search"></span></span> | ||
<input type="text" class="form-control youtube-query" placeholder="Search"> | ||
<span class="input-group-btn"> | ||
<button class="btn btn-default" type="button" data-toggle="clearResults"><span class="glyphicon glyphicon-remove" ></span></button> | ||
</span> | ||
</div> | ||
</form> | ||
</div> | ||
{{#if param 'viewToggle'}} | ||
<div class="col-md-{{param 'viewToggleWidth'}}"> | ||
<div class="btn-group"> | ||
<button data-action="show-list" type="button" class="btn btn-default {{#if sessionIs 'songView' 'list'}}active{{/if}}"><span class="glyphicon glyphicon-list"></span></button> | ||
<button data-action="show-grid" type="button" class="btn btn-default {{#if sessionIs 'songView' 'grid'}}active{{/if}}"><span class="glyphicon glyphicon-picture"></span></button> | ||
</div> | ||
</div> | ||
{{/if}} | ||
{{> searchResults}} | ||
</div> | ||
{{#if error}} | ||
<div class="alert alert-info alert-dismissable"> | ||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button> | ||
{{error}} | ||
</div> | ||
{{/if}} | ||
</template> | ||
|
||
<template name="searchResults"> | ||
{{#with result}} | ||
<ul class="list-group" id="searchresults"> | ||
{{#each items}} | ||
{{> searchResult}} | ||
{{/each}} | ||
</ul> | ||
{{/with}} | ||
</template> | ||
|
||
<template name="searchResult"> | ||
<li class="youtube-result list-group-item"> | ||
<div class="floatleft overlay-container" style="margin-right:20px"> | ||
<p class="duration-container">{{formatDuration contentDetails.duration}}</p> | ||
<img src="{{snippet.thumbnails.default.url}}" alt="thumbnail" /> | ||
</div> | ||
<div> | ||
<h4 class="list-group-item-heading">{{snippet.title}}</h4> | ||
<button type="submit" id="quickAdd" class="btn btn-primary btn-xs floatright"> | ||
<span class="glyphicon glyphicon-plus"></span> | ||
</button> | ||
<button type="submit" id="quickLove" class="btn btn-danger btn-xs floatright"> | ||
<span class="glyphicon glyphicon-heart"></span> | ||
</button> | ||
<span class="glyphicon glyphicon-thumbs-up green bold"></span> {{statistics.likeCount}} | ||
<span class="glyphicon glyphicon-thumbs-down red bold"></span> {{statistics.dislikeCount}} | ||
<a rel="external" href="http://youtu.be/{{id}}"> | ||
<span class="glyphicon glyphicon-link"></span> | ||
</a> | ||
</div> | ||
</li> | ||
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
// initialize active_tab | ||
Session.set('active_tab', 'songs'); | ||
|
||
//Initialisation of searchbar component, | ||
// template example: | ||
// {{> searchBar lovedToggle=0 viewToggle=0 collection="Playlists" playlistId=this._id }} | ||
/*-------------------------- | ||
Available parameters | ||
collection (Collection) Name of the collection to add objects to, requires extra code in | ||
the add function, or sh*t will hit the fan (default: Playlists) | ||
playlistId (int) pointer to which playlist songs should be added (required) | ||
viewToggle (boolean) show/hide the toggle view buttons (default: show) | ||
lovedToggle (boolean) show/hide the add songs from loved songs button (default: show) | ||
---------------------------*/ | ||
|
||
Template.searchBar.created = function( template ) { | ||
//debug purposes | ||
var d = Template.currentData() || {}; //at least return an obj | ||
this.collection = (typeof d.playlistId !== "undefined") ? window[d.collection] : window.Playlists; | ||
this.playlistId = (typeof d.playlistId !== "undefined") ? d.playlistId : null; | ||
this.lovedToggle = (typeof d.lovedToggle !== "undefined") ? d.lovedToggle : 1; | ||
this.viewToggle = (typeof d.viewToggle !== "undefined") ? d.viewToggle : 1; | ||
|
||
|
||
this.lovedToggleWidth = (this.lovedToggle === 1) ? 1 : 0; | ||
this.viewToggleWidth = (this.viewToggle === 1) ? 2 : 0; | ||
this.searchBarWidth = 12 - this.lovedToggleWidth - this.viewToggleWidth; | ||
console.log(this); | ||
|
||
}; | ||
|
||
Template.searchBar.helpers({ | ||
isActiveTab: function(route) { | ||
return Session.equals("active_tab", route) ? "active" : ""; | ||
}, | ||
lovedSongs: function() { | ||
var user = Meteor.user(); | ||
if (!user) | ||
return; | ||
return Songs.find({_id: {$in: user.profile.lovedSongs} }); | ||
}, | ||
//get local template parameters (non-reactive) | ||
param: function(parameter) { | ||
return Template.instance()[parameter] || ""; | ||
} | ||
}); | ||
|
||
|
||
Template.searchBar.events = { | ||
'click ul.playlist-tabs > li': function (e) { | ||
var li = $(e.currentTarget); | ||
var route = li.data('id'); | ||
Session.set("active_tab", route); | ||
}, | ||
'submit form.youtube-search': function (e) { | ||
e.preventDefault(); | ||
}, | ||
'input input.youtube-query': function (e) { | ||
var input = $(e.currentTarget); | ||
youtubeSearch(input.val()); | ||
}, | ||
//hide/show results window | ||
'focus input.youtube-query' : function(e) { | ||
var results = $("#searchresults"); | ||
if(!results.is(":visible")) { | ||
results.fadeIn('fast'); | ||
} | ||
}, | ||
'click button[data-toggle="clearResults"]': function(e) { | ||
$("input.youtube-query").val(""); | ||
searchResults = null; | ||
searchResultsDependency.changed(); | ||
$("input.youtube-query").focus(); | ||
}, | ||
|
||
//add search result to Meteor collection | ||
'click .youtube-result, click a.loved': function(e, template) { | ||
|
||
var videoId = (this.id !== undefined) ? this.id : this._id; | ||
|
||
//see if search bar is linked up to a collection | ||
if (!template.collection) { console.warn("no collection defined"); return; } | ||
if (!template.playlistId) { console.warn("no collection id defined"); return; } | ||
|
||
console.log('queue video:', videoId, 'to playlist', template.playlistId); | ||
$("input.youtube-query").focus(); | ||
var songObject = { | ||
"added" : new Date(), | ||
"author" : Meteor.userId(), | ||
"songId" : videoId | ||
}; | ||
template.collection.update( | ||
{ _id: template.playlistId}, | ||
{ $push: { songs: songObject} } | ||
); | ||
}, | ||
|
||
//Loved a song! | ||
'click button#quickLove': function(e) { | ||
e.stopPropagation(); | ||
console.log("add "+this.snippet.title+" to loved songs"); | ||
var user = Meteor.users; //loved songs are stored in users profile | ||
user.update( | ||
{ _id: Meteor.userId() }, | ||
{ $addToSet : { 'profile.lovedSongs': this.id.videoId }} | ||
); | ||
}, | ||
'click [data-action="show-list"]' : function() { | ||
Session.set("songView","list"); | ||
}, | ||
'click [data-action="show-grid"]' : function() { | ||
Session.set("songView","grid"); | ||
} | ||
}; | ||
|
||
// Youtube search results | ||
var searchTimer; | ||
var searchDelay = 250; // ms | ||
|
||
function youtubeSearch(value) { | ||
clearTimeout(searchTimer); | ||
searchTimer = setTimeout(function() { | ||
if (!value) { | ||
SsearchResults = null; | ||
searchError = 'Please search for something'; | ||
searchResultsDependency.changed(); | ||
return; | ||
} | ||
console.log('youtube search for:', value); | ||
youtubeSearchQuery({ | ||
part: 'id,snippet', | ||
type: 'video', | ||
videoEmbeddable: 'true', | ||
q: value | ||
}); | ||
},searchDelay); | ||
} | ||
|
||
var searchError; | ||
var searchResults; | ||
var searchResultsDependency = new Deps.Dependency(); | ||
|
||
youtubeSearchQuery = function(options) { | ||
options = options || {}; | ||
var currentSearchTimer = searchTimer; | ||
|
||
Meteor.call('youtube_search', options, function(error, data) { | ||
if (error) { | ||
console.log('Youtube search API error:', error); | ||
} else { | ||
console.log('Youtube search API result:', data); | ||
} | ||
searchError = error; | ||
searchResults = data; // TODO: search result data != videoQuery data | ||
searchResultsDependency.changed(); | ||
|
||
if (currentSearchTimer != searchTimer) { | ||
console.warn('cancel video query'); | ||
return; | ||
} | ||
|
||
// query for more details (needs optimization) | ||
var ids = _.pluck(_.pluck(data.items, 'id'), 'videoId'); | ||
console.log('load more data for ', ids); | ||
|
||
youtubeVideoQuery({ | ||
'part': 'snippet,contentDetails,statistics', | ||
'id': ids, | ||
}); | ||
}); | ||
}; | ||
|
||
youtubeVideoQuery = function(options) { | ||
options = options || {}; | ||
|
||
Meteor.call('youtube_videos_list', options, function(error, data) { | ||
if (error) { | ||
console.log('Youtube list API error:', error); | ||
} else { | ||
searchResults = data; // TODO: search result data != videoQuery data | ||
searchResultsDependency.changed(); | ||
} | ||
}); | ||
}; | ||
|
||
Template.searchResults.result = function() { | ||
Deps.depend(searchResultsDependency); | ||
return searchResults; | ||
}; | ||
|
||
Template.searchBar.error = function() { | ||
Deps.depend(searchResultsDependency); | ||
return searchError; | ||
}; |
Oops, something went wrong.