563 lines
20 KiB
CoffeeScript
563 lines
20 KiB
CoffeeScript
context = window
|
|
MIX_MODES = context.JK.MIX_MODES
|
|
|
|
|
|
@JamTrackSearchScreen = React.createClass({
|
|
|
|
mixins: [Reflux.listenTo(@AppStore,"onAppInit"), Reflux.listenTo(@UserStore,"onUserChanged")]
|
|
|
|
LIMIT: 10
|
|
instrument_logo_map: context.JK.getInstrumentIconMap24()
|
|
input: null
|
|
MAX_ARTIST_SHOW: 3
|
|
|
|
filterOption:() ->
|
|
true
|
|
|
|
render: () ->
|
|
|
|
searchText = if @state.first_search then 'SEARCH' else 'SEARCH AGAIN'
|
|
|
|
uiJamTracks = []
|
|
for jamtrack in @state.jamtracks
|
|
trackRow = context._.clone(jamtrack)
|
|
trackRow.track_cnt = jamtrack.tracks.length
|
|
trackRow.tracks = []
|
|
|
|
# if an instrument is selected by the user, then re-order any jam tracks with a matching instrument to the top
|
|
|
|
###instrument = @instrument.val()
|
|
if instrument?
|
|
jamtrack.tracks.sort((a, b) =>
|
|
aWeight = @computeWeight(a, instrument)
|
|
bWeight = @computeWeight(b, instrument)
|
|
return aWeight - bWeight
|
|
)
|
|
###
|
|
for track in jamtrack.tracks
|
|
|
|
if track.track_type == 'Master' || track.track_type == 'Track'
|
|
trackRow.tracks.push(track)
|
|
|
|
if track.track_type == 'Master'
|
|
track.instrument_desc = "Master"
|
|
else if track.track_type == 'Track'
|
|
inst = '../assets/content/icon_instrument_default24.png'
|
|
if track.instrument?
|
|
if track.instrument.id in @instrument_logo_map
|
|
inst = @instrument_logo_map[track.instrument.id].asset
|
|
track.instrument_desc = track.instrument.description
|
|
track.instrument_url = inst
|
|
|
|
if track.part != ''
|
|
track.instrument_desc += ' (' + track.part + ')'
|
|
|
|
trackRow.free_state = if @state.is_free then 'free' else 'non-free'
|
|
|
|
trackRow.is_free = @state.is_free
|
|
|
|
uiJamTracks.push trackRow
|
|
|
|
artists = []
|
|
artistsShown = 0
|
|
for artist in @state.artists
|
|
|
|
if @state.show_all_artists || artistsShown < @MAX_ARTIST_SHOW
|
|
artists.push `<div key={artist.original_artist}><a className="show-artist" onClick={this.artistNavSelected} data-artist={artist.original_artist}>{artist.original_artist}</a></div>`
|
|
|
|
artistsShown += 1
|
|
|
|
|
|
artists.push `<div key="no-results" className="no-results">No matching artists</div>` if artists.length == 0
|
|
|
|
if !@state.show_all_artists && @state.artists.length > @MAX_ARTIST_SHOW
|
|
artists.push `<div key="show-hide-artists"><a onClick={this.showAllArtists} className="show-hide-artists">show all <div className="details-arrow arrow-down" /></a></div>`
|
|
else if @state.show_all_artists
|
|
artists.push `<div key="show-hide-artists"><a onClick={this.hideExtraArtists} className="show-hide-artists">hide artists <div className="details-arrow arrow-up" /></a></div>`
|
|
|
|
jamtracks = []
|
|
|
|
|
|
for jamtrack in uiJamTracks
|
|
|
|
jamtrackPricesClasses = { "jamtrack-price" : true }
|
|
jamtrackPricesClasses[jamtrack.free_state] = true
|
|
jamtrackPricesClasses = classNames(jamtrackPricesClasses)
|
|
|
|
tracks = []
|
|
for track in jamtrack.tracks
|
|
tracks.push `<div className="jamtrack-track hidden" key={track.id} data-jamtrack-track-id={track.id}>
|
|
<div className="jamtrack-preview">
|
|
<JamTrackPreview jamTrack={jamtrack} jamTrackTrack={track} options={{master_shows_duration: true, color:'gray'}} />
|
|
<div className="clearall" />
|
|
</div>
|
|
</div>`
|
|
|
|
actionBtn = null
|
|
if jamtrack.purchased
|
|
actionBtn = `<a className="jamtrack-add-cart-disabled button-grey button-disabled" href="javascript:void(0)">PURCHASED</a>`
|
|
else if jamtrack.is_free
|
|
actionBtn = `<a className="jamtrack-add-cart button-orange is_free" href="#" data-jamtrack-id={jamtrack.id}> GET IT FREE!</a>`
|
|
else if jamtrack.added_cart
|
|
actionBtn = `<a className="jamtrack-add-cart-disabled button-grey button-disabled" href="client#/shoppingCart">ALREADY IN CART</a>`
|
|
else
|
|
actionBtn = `<a className="jamtrack-add-cart button-orange" href="#" data-jamtrack-id={jamtrack.id}>ADD TO CART</a>`
|
|
|
|
availabilityNotice = null
|
|
if jamtrack.sales_region==context.JK.AVAILABILITY_US
|
|
availabilityNotice =
|
|
`<div className="jamtrack-license">
|
|
This JamTrack available only to US customers.
|
|
<a className="license-us-why" href="#">why?</a>
|
|
</div>`
|
|
|
|
jamtracks.push `<tr className="jamtrack-record" key={jamtrack.id} data-jamtrack-id={jamtrack.id}>
|
|
<td className="jamtrack-detail">
|
|
<div className="jamtrack-name">"{jamtrack.name}"</div>
|
|
<div className="jamtrack-original-artist">by {jamtrack.original_artist}</div>
|
|
<br className="clearall"/>
|
|
<div className="clearall detail-label extra hidden song-writer">Songwriters:</div>
|
|
<div className="detail-value extra hidden">{jamtrack.songwriter}</div>
|
|
<div className="clearall detail-label extra hidden">Publishers:</div>
|
|
<div className="detail-value extra hidden">{jamtrack.publisher}</div>
|
|
<div className="clearall detail-label extra hidden">Genres:</div>
|
|
<div className="detail-value extra hidden">{jamtrack.genres.join(', ')}</div>
|
|
<div className="clearall detail-label extra hidden">Version:</div>
|
|
<div className="detail-value extra hidden">{jamtrack.recording_type}</div>
|
|
</td>
|
|
<td className="jamtrack-tracks">
|
|
<div className="detail-arrow">
|
|
<div className="jamtrack-detail-btn">
|
|
show all tracks <a className="details-arrow arrow-down"/>
|
|
</div>
|
|
</div>
|
|
{tracks}
|
|
</td>
|
|
<td className="jamtrack-action">
|
|
<div className="jamtrack-action-container">
|
|
<div className="jamtrack-actions">
|
|
<div className={jamtrackPricesClasses}>$ {jamtrack.price}</div>
|
|
{actionBtn}
|
|
{availabilityNotice}
|
|
</div>
|
|
</div>
|
|
|
|
</td>
|
|
</tr>`
|
|
|
|
#jamtracks.push `<div className="no-results">No matching JamTracks</div>` if jamtracks.length == 0
|
|
|
|
searchClasses = classNames({
|
|
"button-orange" : true,
|
|
"search-btn" : true,
|
|
"disabled" : @state.searching
|
|
})
|
|
|
|
artistSection = null
|
|
jamTracksSection = null
|
|
|
|
if @state.type == 'user-input'
|
|
if @state.searching
|
|
jamtracksHeader = "searching..."
|
|
else
|
|
jamtracksHeader = "search results: #{@state.count} jamtracks"
|
|
|
|
|
|
|
|
else if @state.type == 'artist-select'
|
|
jamtracksHeader = "search results: jamtracks for artist \"#{@state.artist}\""
|
|
else if @state.type == 'song-select'
|
|
jamtracksHeader = "search results: jamtrack \"#{@state.song}\""
|
|
else
|
|
throw "unknown search type #{@state.type}"
|
|
|
|
if !@state.first_search
|
|
|
|
|
|
# only show the artists links if the user typed the results
|
|
if @state.type == 'user-input'
|
|
artistSection =
|
|
`<div>
|
|
<h2>search results: artists</h2>
|
|
<div className="artist-results">
|
|
{artists}
|
|
</div>
|
|
</div>`
|
|
|
|
jamTracksSection =
|
|
`<div>
|
|
<h2 className="jamtrack-results-header">{jamtracksHeader} <a className="back-to-jamtracks-home" href="/client#/jamtrack">back to jamtracks home</a></h2>
|
|
|
|
<table className="generaltable jamtrack-table">
|
|
<thead>
|
|
<tr>
|
|
<th className="jamtrack-detail">JAMTRACK</th>
|
|
<th className="jamtrack-tracks">TRACKS INCLUDED / PREVIEW</th>
|
|
<th className="jamtrack-shop">SHOP</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody className="jamtrack-content">
|
|
{jamtracks}
|
|
</tbody>
|
|
</table>
|
|
<div className="end-of-jamtrack-list end-of-list">No more JamTracks</div>
|
|
</div>`
|
|
|
|
options = {}
|
|
|
|
|
|
searchValue = if @state.search == 'SEPARATOR' then '' else window.JamTrackSearchInput
|
|
|
|
`<div className="JamTrackSearchScreen">
|
|
<div className="controls">
|
|
<JamTrackAutoComplete onSearch={this.search} />
|
|
<button className={searchClasses} name="search" onClick={this.userSearch}>{searchText}</button>
|
|
</div>
|
|
<div className="content-body-scroller">
|
|
{artistSection}
|
|
{jamTracksSection}
|
|
</div>
|
|
</div>`
|
|
|
|
|
|
|
|
clearResults:() ->
|
|
@setState({currentPage: 0, next: null, show_all_artists: false, artists:[], jamtracks:[], type: 'user-input', searching:false, artist: null, song:null, is_free: @user.show_free_jamtrack, first_search: true})
|
|
|
|
|
|
getInitialState: () ->
|
|
{search: '', type: 'user-input', artists:[], jamtracks:[], show_all_artists: false, currentPage: 0, next: null, searching: false, first_search: true, count: 0, is_free: context.JK.currentUserFreeJamTrack}
|
|
|
|
onSelectChange: (val) ->
|
|
|
|
return false unless val?
|
|
|
|
search_type
|
|
if val.indexOf('ARTIST=') == 0
|
|
search_type = 'artist-select'
|
|
artist = val['ARTIST='.length..-1]
|
|
@search(search_type, artist)
|
|
else if val.indexOf('SONG=') == 0
|
|
search_type = 'song-select'
|
|
song = val['SONG='.length..-1]
|
|
@search(search_type, song)
|
|
else
|
|
@logger.debug("user selected separator")
|
|
# this is to signal to the component that the separator was selected, and it has code in render to negate the selection
|
|
setTimeout((() =>
|
|
@setState({search:val})
|
|
), 1)
|
|
|
|
return false
|
|
|
|
|
|
onSelectBlur: (e) ->
|
|
|
|
#@logger.debug("blur time")
|
|
|
|
#@search()
|
|
|
|
showAllArtists: () ->
|
|
@setState({show_all_artists: true})
|
|
|
|
hideExtraArtists: () ->
|
|
@setState({show_all_artists: false})
|
|
|
|
|
|
defaultQuery:(extra) ->
|
|
query =
|
|
per_page: @LIMIT
|
|
page: @state.currentPage + 1
|
|
sort_by: 'jamtrack'
|
|
if @state.next
|
|
query.since = @state.next
|
|
$.extend(query, extra)
|
|
|
|
|
|
|
|
userSearch: (e) ->
|
|
e.preventDefault()
|
|
@search('user-input', window.JamTrackSearchInput)
|
|
|
|
search: (search_type, input) ->
|
|
return if @state.searching
|
|
return unless input?
|
|
|
|
window.JamTrackSearchInput = input
|
|
|
|
$root = $(@getDOMNode())
|
|
# disable scroll watching now that we've started a new search
|
|
#@logger.debug("disabling infinite scroll")
|
|
$root.find('.content-body-scroller').off('scroll')
|
|
$root.find('.end-of-jamtrack-list').hide()
|
|
|
|
artistSearch = {limit:100}
|
|
if search_type == 'artist-select'
|
|
# the user wants to see just artists matching thes exact name
|
|
artistSearch.artist = input
|
|
else
|
|
# the user wants to see anything sort of matching input
|
|
artistSearch.artist_search = input
|
|
|
|
if input?
|
|
@rest.getJamTrackArtists(artistSearch)
|
|
.done((response) =>
|
|
@setState({artists:response.artists})
|
|
|
|
# we have to make sure the query starts from page 1, and no 'next' from previous causes a 'since' to show up
|
|
query = @defaultQuery({page: 1})
|
|
delete query.since
|
|
|
|
@logger.debug("Search type", search_type)
|
|
if search_type == 'artist-select'
|
|
query.artist = input # works like exact match
|
|
else if search_type == 'song-select'
|
|
query.song = input # works as exact match
|
|
|
|
else
|
|
query.search = input # works with tsv
|
|
@rest.getJamTracks(query)
|
|
.done((response) =>
|
|
@setState({jamtracks: response.jamtracks, next: response.next, searching: false, first_search: false, currentPage: 1, count: response.count})
|
|
)
|
|
.fail(() =>
|
|
@app.notifyServerError jqXHR, 'Search Unavailable'
|
|
@setState({searching: false, first_search: false})
|
|
)
|
|
)
|
|
.fail(() =>
|
|
@app.notifyServerError jqXHR, 'Search Unavailable'
|
|
@setState({searching: false, first_search: false})
|
|
)
|
|
|
|
@setState({currentPage: 0, next: null, artists: [], jamtracks:[], searching: true, artist: input, song: input, type: search_type, search:input, count:0})
|
|
|
|
getOptions: (input, callback) =>
|
|
|
|
#@logger.debug("getOptions input #{input}", this)
|
|
|
|
# sigh. ugly global
|
|
window.JamTrackSearchInput = input
|
|
|
|
if !input? || input.length == 0
|
|
callback(null, {options: [], complete: false})
|
|
return
|
|
|
|
@rest.autocompleteJamTracks({match:input, limit:5})
|
|
.done((autocomplete) =>
|
|
|
|
options = []
|
|
for artist in autocomplete.artists
|
|
options.push { value: "ARTIST=#{artist.original_artist}", label: "Artist: #{artist.original_artist}" }
|
|
|
|
if options.length > 0 && autocomplete.songs.length > 0
|
|
options.push { value: 'SEPARATOR', label: "---------------"}
|
|
|
|
for jamtrack in autocomplete.songs
|
|
options.push { value: "SONG=#{jamtrack.name}", label: "Song: #{jamtrack.name}" }
|
|
|
|
callback(null, {options: options, complete: false})
|
|
)
|
|
|
|
artistNavSelected: (e) ->
|
|
e.preventDefault()
|
|
|
|
@search('artist-select', $(e.target).attr('data-artist'))
|
|
|
|
componentDidMount: () ->
|
|
#@logger.debug("componentDidMount")
|
|
|
|
componentDidUpdate: ( ) ->
|
|
$root = $(this.getDOMNode())
|
|
$scroller = $root.find('.content-body-scroller')
|
|
|
|
for jamTrack in @state.jamtracks
|
|
jamtrackElement = $root.find("tbody .jamtrack-record[data-jamtrack-id=\"#{jamTrack.id}\"]")
|
|
alreadyRegistered = jamtrackElement.data('registered')
|
|
|
|
unless alreadyRegistered
|
|
jamtrackElement.data('jamTrack', jamTrack)
|
|
jamtrackElement.data('registered', true)
|
|
jamtrackElement.data('expanded', true)
|
|
|
|
@handleExpanded(jamtrackElement)
|
|
@registerEvents(jamtrackElement)
|
|
|
|
|
|
if @state.next == null
|
|
$scroller = $root.find('.content-body-scroller')
|
|
# if we less results than asked for, end searching
|
|
#$scroller.infinitescroll 'pause'
|
|
#@logger.debug("disabling infinite scroll")
|
|
$scroller.off('scroll')
|
|
if @state.currentPage == 1 and @state.jamtracks.length == 0
|
|
$root.find('.end-of-jamtrack-list').text('No JamTracks found matching your search').show()
|
|
@logger.debug("JamTrackSearch: empty search")
|
|
else if @state.currentPage > 0
|
|
@logger.debug("end of search")
|
|
$noMoreJamtracks = $root.find('.end-of-jamtrack-list').text('No more JamTracks').show()
|
|
# there are bugs with infinitescroll not removing the 'loading'.
|
|
# it's most noticeable at the end of the list, so whack all such entries
|
|
else
|
|
@registerInfiniteScroll($scroller)
|
|
|
|
|
|
|
|
registerInfiniteScroll:($scroller) ->
|
|
@logger.debug("registering infinite scroll")
|
|
$scroller.off('scroll')
|
|
$scroller.on('scroll', () =>
|
|
|
|
# be sure to not fire off many refreshes when user hits the bottom
|
|
return if @refreshing
|
|
|
|
if $scroller.scrollTop() + $scroller.innerHeight() + 100 >= $scroller[0].scrollHeight
|
|
$scroller.append('<div class="infinite-scroll-loader-2">... Loading more JamTracks ...</div>')
|
|
@refreshing = true
|
|
@setState({searching: true})
|
|
@logger.debug("refreshing more jamtracks for infinite scroll")
|
|
@rest.getJamTracks(@defaultQuery({search:@state.search}))
|
|
.done((json) =>
|
|
@setState({jamtracks: @state.jamtracks.concat(json.jamtracks), next: json.next, first_search: false, currentPage: @state.currentPage + 1, count: json.count})
|
|
)
|
|
.always(() =>
|
|
$scroller.find('.infinite-scroll-loader-2').remove()
|
|
@refreshing = false
|
|
@setState({searching: false})
|
|
)
|
|
)
|
|
|
|
playJamtrack:(e) ->
|
|
e.preventDefault()
|
|
|
|
addToCartJamtrack:(e) ->
|
|
e.preventDefault()
|
|
$target = $(e.target)
|
|
params = id: $target.attr('data-jamtrack-id')
|
|
isFree = $(e.target).is('.is_free')
|
|
|
|
@rest.addJamtrackToShoppingCart(params).done((response) =>
|
|
if context.JK.currentUserId?
|
|
if isFree
|
|
if @user.has_redeemable_jamtrack
|
|
# this is the 1st jamtrack; let's user the user to completion
|
|
context.location = '/client#/redeemComplete'
|
|
else
|
|
# this is must be a user's gifted jamtrack, to treat them normally in that they'll go to the shopping cart
|
|
#context.location = '/client#/shoppingCart'
|
|
context.location = '/client#/redeemComplete'
|
|
else
|
|
# this user has nothing free; so send them to shopping cart
|
|
context.location = '/client#/shoppingCart'
|
|
else
|
|
if isFree
|
|
# user not logged in; make them signup
|
|
context.location = '/client#/redeemSignup'
|
|
else
|
|
# this user has nothing free; so send them to shopping cart
|
|
context.location = '/client#/shoppingCart'
|
|
|
|
|
|
).fail(((jqxhr) =>
|
|
|
|
handled = false
|
|
if jqxhr.status == 422
|
|
body = JSON.parse(jqxhr.responseText)
|
|
if body.errors && body.errors.base
|
|
handled = true
|
|
context.JK.Banner.showAlert("You can not have a mix of free and non-free items in your shopping cart.<br/><br/>If you want to add this new item to your shopping cart, then clear out all current items by clicking on the shopping cart icon and clicking 'delete' next to each item.")
|
|
if !handled
|
|
@app.ajaxError(arguments[0], arguments[1], arguments[2])
|
|
|
|
))
|
|
|
|
licenseUSWhy:(e) ->
|
|
e.preventDefault()
|
|
@app.layout.showDialog 'jamtrack-availability-dialog'
|
|
|
|
registerEvents:($parent) ->
|
|
$parent.find('.play-button').on 'click', @playJamtrack
|
|
$parent.find('.jamtrack-add-cart').on 'click', @addToCartJamtrack
|
|
$parent.find('.license-us-why').on 'click', @licenseUSWhy
|
|
$parent.find('.jamtrack-detail-btn').on 'click', @toggleExpanded
|
|
|
|
toggleExpanded:(e) ->
|
|
e.preventDefault()
|
|
jamtrackRecord = $(e.target).parents('.jamtrack-record')
|
|
@handleExpanded(jamtrackRecord)
|
|
|
|
handleExpanded:(trackElement) ->
|
|
jamTrack = trackElement.data('jamTrack')
|
|
expanded = trackElement.data('expanded')
|
|
expand = !expanded
|
|
trackElement.data('expanded', expand)
|
|
|
|
detailArrow = trackElement.find('.jamtrack-detail-btn')
|
|
|
|
if expand
|
|
trackElement.find('.extra').removeClass('hidden')
|
|
detailArrow.html('hide tracks <a class="details-arrow arrow-up"></a>')
|
|
for track in jamTrack.tracks
|
|
trackElement.find("[data-jamtrack-track-id='#{track.id}']").removeClass('hidden')
|
|
else
|
|
trackElement.find('.extra').addClass('hidden')
|
|
detailArrow.html('show all tracks <a class="details-arrow arrow-down"></a>')
|
|
count = 0
|
|
for track in jamTrack.tracks
|
|
if count < 6
|
|
trackElement.find("[data-jamtrack-track-id='#{track.id}']").removeClass('hidden')
|
|
else
|
|
trackElement.find("[data-jamtrack-track-id='#{track.id}']").addClass('hidden')
|
|
count++
|
|
|
|
|
|
afterShow: (data) ->
|
|
|
|
@setFilterFromURL()
|
|
|
|
setFilterFromURL:() ->
|
|
|
|
performSearch = false
|
|
if $.QueryString['artist']?
|
|
performSearch = true
|
|
@search('artist-select', $.QueryString['artist'])
|
|
else if $.QueryString['song']?
|
|
performSearch = true
|
|
@search('song-select', $.QueryString['song'])
|
|
else if $.QueryString['search']?
|
|
performSearch = true
|
|
@search('user-input', $.QueryString['search'])
|
|
else
|
|
# check if someone has requested a search for us as we transition to this screen
|
|
search = context.JamTrackStore.checkRequestedSearch()
|
|
if search?
|
|
performSearch = true
|
|
@search(search.searchType, search.searchData)
|
|
|
|
if performSearch
|
|
if window.history.replaceState #ie9 proofing
|
|
window.history.replaceState({}, "", "/client#/jamtrack/search")
|
|
|
|
|
|
beforeShow: () ->
|
|
if !@state.first_search
|
|
@search(@state.type, window.JamTrackSearchInput)
|
|
|
|
onAppInit: (@app) ->
|
|
|
|
window.JamTrackSearchInput = '' # need to be not null; otherwise react-select chokes
|
|
@EVENTS = context.JK.EVENTS
|
|
@rest = context.JK.Rest()
|
|
@logger = context.JK.logger
|
|
|
|
screenBindings =
|
|
'beforeShow': @beforeShow
|
|
'afterShow': @afterShow
|
|
|
|
@app.bindScreen('jamtrack/search', screenBindings)
|
|
|
|
onUserChanged: (userState) ->
|
|
@user = userState?.user
|
|
@setState({is_free: @user?.show_free_jamtrack})
|
|
|
|
}) |