539 lines
18 KiB
CoffeeScript
539 lines
18 KiB
CoffeeScript
context = window
|
|
MIX_MODES = context.JK.MIX_MODES
|
|
|
|
|
|
@JamTrackSearchScreen = React.createClass({
|
|
|
|
mixins: [Reflux.listenTo(@AppStore,"onAppInit")]
|
|
|
|
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
|
|
trackRow.tracks.push(track)
|
|
if track.track_type=='Master'
|
|
track.instrument_desc = "Master"
|
|
else
|
|
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.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.purchased
|
|
actionBtn = `<a className="jamtrack-add-cart-disabled button-grey button-disabled" href="javascript:void(0)">PURCHASED</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: context.JK.currentUserFreeJamTrack, 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) ->
|
|
#@logger.debug("CHANGE #{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(isFree)
|
|
if context.JK.currentUserId?
|
|
context.JK.currentUserFreeJamTrack = true # make sure the user sees no more free notices
|
|
context.location = '/client#/redeemComplete'
|
|
else
|
|
# now make a rest call to buy it
|
|
context.location = '/client#/redeemSignup'
|
|
|
|
else
|
|
context.location = '/client#/shoppingCart'
|
|
|
|
).fail(() => @app.ajaxError)
|
|
|
|
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: () ->
|
|
@setState({is_free: context.JK.currentUserFreeJamTrack})
|
|
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)
|
|
}) |