This commit is contained in:
Seth Call 2015-08-11 05:49:48 -05:00
parent 795f0b5375
commit 0bb635cf5d
18 changed files with 604 additions and 202 deletions

View File

@ -299,4 +299,5 @@ enhance_band_profile.sql
alter_band_profile_rate_defaults.sql
repair_band_profile.sql
jam_track_onboarding_enhancements.sql
jam_track_name_drop_unique.sql
jam_track_name_drop_unique.sql
jam_track_searchability.sql

View File

@ -0,0 +1,6 @@
ALTER TABLE jam_tracks ADD COLUMN search_tsv tsvector;
CREATE TRIGGER tsvectorupdate BEFORE INSERT OR UPDATE
ON jam_tracks FOR EACH ROW EXECUTE PROCEDURE
tsvector_update_trigger(search_tsv, 'public.jamenglish', original_artist, name, additional_info);
CREATE INDEX jam_track_search_tsv_index ON jam_tracks USING gin(search_tsv);
UPDATE jam_tracks SET original_artist=original_artist, name=name, additional_info=additional_info;

View File

@ -252,6 +252,13 @@ module JamRuby
query = query.where("jam_track_rights.user_id = ?", user.id)
end
if options[:search]
tsquery = Search.create_tsquery(options[:search])
if tsquery
query = query.where("(search_tsv @@ to_tsquery('jamenglish', ?))", tsquery)
end
end
if options[:artist].present?
query = query.where("original_artist=?", options[:artist])
end

View File

@ -83,7 +83,7 @@ module JamRuby
@search_type = :musicians
User.musicians
end
@results = rel.where("(name_tsv @@ to_tsquery('jamenglish', ?))", tsquery).limit(10)
@results = rel.where("(name_c @@ to_tsquery('jamenglish', ?))", tsquery).limit(10)
@results
end
@ -139,6 +139,7 @@ module JamRuby
args = args + ":*"
args
end
def order_param(params, keys=M_ORDERING_KEYS)
ordering = params[:orderby]
ordering.blank? ? keys[0] : keys.detect { |oo| oo.to_s == ordering }

View File

@ -215,6 +215,25 @@ describe JamTrack do
query.size.should == 1
query[0].should eq(jam_track1)
end
it "full text search" do
jam_track1 = FactoryGirl.create(:jam_track_with_tracks, name: 'Take a Chance On Me', original_artist: 'ABBA')
jam_track2 = FactoryGirl.create(:jam_track_with_tracks, name: 'Nothing Chance', original_artist: 'ABBA')
query, pager = JamTrack.index({search: 'Take'}, user)
query.size.should == 1
query[0].should eq(jam_track1)
query, pager = JamTrack.index({search: 'ABB'}, user)
query.size.should == 2
query, pager = JamTrack.index({search: 'Chance'}, user)
query.size.should == 2
query, pager = JamTrack.index({search: 'Chan'}, user)
query.size.should == 2
end
end
describe "validations" do

View File

@ -97,6 +97,7 @@ gem 'react-rails', '~> 1.0'
source 'https://rails-assets.org' do
gem 'rails-assets-reflux'
gem 'rails-assets-classnames'
gem 'rails-assets-react-select'
end
#group :development, :production do

View File

@ -17,7 +17,8 @@ context.JK.JamTrackLanding = class JamTrackLanding
screenBindings =
'beforeShow': @beforeShow
'afterShow': @afterShow
@app.bindScreen('jamtrackLanding', screenBindings)
#@app.bindScreen('jamtrackLanding', screenBindings)
@screen = $('#jamtrackLanding')
@noFreeJamTrack = @screen.find('.no-free-jamtrack')
@freeJamTrack = @screen.find('.free-jamtrack')

View File

@ -1,3 +1,5 @@
//= require react-input-autosize
//= require react-select
//= require_directory ./react-components/helpers
//= require_directory ./react-components/actions
//= require ./react-components/stores/AppStore
@ -13,4 +15,5 @@
//= require_directory ./react-components/stores
//= require_directory ./react-components/mixins
//= require_directory ./react-components
//= require_directory ./react-components/landing
//= require_directory ./react-components/landing

View File

@ -0,0 +1,139 @@
context = window
MIX_MODES = context.JK.MIX_MODES
@JamTrackLandingScreen = React.createClass({
mixins: [Reflux.listenTo(@AppStore,"onAppInit")]
getInitialState: () ->
{user: null}
render: () ->
howTo = null
if @state.user?.free_jamtrack
howTo =
`<div className="no-free-jamtrack">
<span>For a limited time, get one JamTrack free. Search JamTracks below, add one to your shopping cart, and we'll make it free during the checkout process.</span>
</div>`
else
howTo =
`<div className="free-track">
<span>
To play with your JamTracks, open a JamTrack while in a session in the JamKazam app. Or <a href="/client#/account/jamtracks">visit the JamTracks Section of your account.</a>
</span>
</div>`
`<div className="content-body-scroller">
<div className="list-columns">
<div className="browse">
<h2>my jamtracks</h2>
<div className="howto">
{howTo}
</div>
<h2 className="browse-jamtracks">search jamtracks</h2>
</div>
<div className="about">
<h2>what are jamtracks?</h2>
<div className="what">
<div className="details">
JamTracks are the best way to play along with your favorite music! Unlike traditional backing tracks, JamTracks are professionally mastered, complete multitrack recordings, with fully isolated tracks for each part of the master mix. Used with the free JamKazam app & Internet service, you can:
</div>
<ul>
<li>Solo just the part you want to play in order to hear and learn it</li>
<li>Mute just the part you want to play and play along with the rest</li>
<li>Make audio recordings and share them via Facebook or URL</li>
<li>Make video recordings and share them via YouTube</li>
<li>And even go online to play with others in real time -- for example, you can play the electric guitar lead, while someone else plays the bass, and all other parts play from the recorded tracks in your session</li>
</ul>
<a className="video-thumbnail" href="https://www.youtube.com/watch?v=askHvcCoNfw" rel="external">
<img className="play" src="/assets/content/icon_youtube_play.png" />
</a>
</div>
</div>
</div>
</div>`
componentDidMount: () ->
logger.debug("componentDidMount")
afterShow: (data) ->
if context.JK.currentUserId
@app.user().done(@onUser)
else
@onUser({free_jamtrack: gon.global.one_free_jamtrack_per_user})
beforeShow: () ->
@setState({user: null})
onUser:(user) ->
@setState({user: user})
# Get artist names and build links
@rest.getJamTrackArtists({group_artist: true, per_page:100})
.done(this.buildArtistLinks)
.fail(this.handleFailure)
# Bind links to action that will open the jam_tracks list view filtered to given artist_name:
# artist_name
@bindArtistLinks()
buildArtistLinks:(response) ->
# Get artist names and build links
@logger.debug("buildArtist links response", response)
artists = response.artists
$("#band_list>li:not('#no_bands_found')").remove()
if artists.length==0
@noBandsFound.removeClass("hidden")
else
@noBandsFound.addClass("hidden")
# client#/jamtrack
for artist in artists
artistLink = "<a href='client?artist=#{encodeURIComponent(artist.original_artist)}#/jamtrackBrowse' class='artist-link' artist='#{artist.original_artist}'>#{artist.original_artist} (#{artist.song_count})</a>"
@bandList.append("<li>#{artistLink}</li>")
# We don't want to do a full page load if this is clicked on here:
bindArtistLinks:() ->
that=this
@bandList.on "click", "a.artist-link", (event)->
context.location="client#/jamtrackBrowse"
if window.history.replaceState # ie9 proofing
window.history.replaceState({}, "", this.href)
event.preventDefault()
onAppInit: (@app) ->
logger.debug("ON APP INI")
@rest = context.JK.Rest()
@client = context.jamClient
@logger = context.JK.logger
@screen = null
@noFreeJamTrack = null
@freeJamTrack = null
@bandList = null
@noBandsFound = null
screenBindings =
'beforeShow': @beforeShow
'afterShow': @afterShow
@app.bindScreen('jamtrackLanding', screenBindings)
@screen = $('#jamtrackLanding')
@noFreeJamTrack = @screen.find('.no-free-jamtrack')
@freeJamTrack = @screen.find('.free-jamtrack')
@bandList = @screen.find('#band_list')
@noBandsFound = @screen.find('#no_bands_found')
})

View File

@ -0,0 +1,78 @@
context = window
MIX_MODES = context.JK.MIX_MODES
@JamTrackSearchScreen = React.createClass({
mixins: [Reflux.listenTo(@AppStore,"onAppInit")]
LIMIT: 10
instrument_logo_map: context.JK.getInstrumentIconMap24()
getInitialState: () ->
{search: null, type: 'user-input'}
logChange: (val) ->
@logger.debug("CHANGE #{val}")
render: () ->
options = [
{ value: 'one', label: 'One' },
{ value: 'two', label: 'Two' }
];
`<div>
<div className="controls">
<Select
name="form-field-name"
value="one"
options={options}
onChange={this.logChange}
/>
</div>
<div className="content-body-scroller">
</div>
</div>`
componentDidMount: () ->
#@logger.debug("componentDidMount")
afterShow: (data) ->
@logger.debug("afterShow")
beforeShow: () ->
@setState({user: null})
onAppInit: (@app) ->
logger.debug("ON APP INI SEARCH SCREEN")
@rest = context.JK.Rest()
@logger = context.JK.logger
@screen = null
@content = null
@scroller = null
@genre = null
@artist = null
@instrument = null
@availability = null
@nextPager = null
@noMoreJamtracks = null
@currentPage = 0
@next = null
#@currentQuery = this.defaultQuery()
@expanded = null
@shownHelperBubbles = false
screenBindings =
'beforeShow': @beforeShow
'afterShow': @afterShow
@app.bindScreen('jamtrack/search', screenBindings)
})

View File

@ -85,4 +85,5 @@
*= require users/signinCommon
*= require landings/partner_agreement_v1
*= require_directory ./react-components
*/

View File

@ -0,0 +1,274 @@
@import 'common';
#jamtrackSearch {
.filter-element.desc {
margin-left:15px;
}
h2 {
font-size: 20px;
color: white;
font-weight: bold;
margin-bottom:10px;
}
.jamtrack-header {
background-color: #4c4c4c;
font-weight: bold;
text-transform: uppercase;
height: 2em;
padding: 4px;
}
a.jamtrack_help {
color: #fff;
text-decoration: none;
margin: 4px 0px 0px 60px;
&:hover {
text-decoration: underline;
}
}
table.jamtrack-table {
table-layout:fixed;
}
.jamtrack-content {
text-align: center;
border: 1px solid #222222;
padding: 2px
}
.no-jamtracks-msg {
margin: 10px 0;
text-align:center;
}
.jamtrack-record {
//border-bottom: 1px solid black;
text-align: left;
}
th.jamtrack-detail {
padding:6px;
}
th.jamtrack-tracks {
padding:6px;
}
th.jamtrack-action {
padding:6px;
text-align:center;
}
td.jamtrack-action {
padding:0;
position:relative;
}
.jamtrack-detail {
@include border_box_sizing;
width: 25%;
padding: 10px 0 0 10px;
.detail-label {
width: 80px;
float: left;
margin-top: 5px;
font-weight: 400;
font-size: 11px;
clear:both;
}
.detail-value {
float: left;
margin-top: 5px;
margin-bottom:15px;
font-size:11px;
}
.copyright-value {
width: 40%;
float: left;
margin-top: 5px;
}
.jamtrack-description {
display: none;
}
}
.jamtrack-detail-btn {
cursor: pointer;
margin-top: 29px;
margin-right: 5px;
padding-top: 5px;
vertical-align: bottom;
color:#fc0;
.arrow-down {
float:none;
margin-left:5px;
margin-top:0;
margin-right:0;
border-top: 4px solid #fc0;
border-left: 4px solid transparent;
border-right: 4px solid transparent;
display:inline-block;
}
.arrow-up {
float:none;
margin-right:0;
margin-left:5px;
margin-bottom:2px;
border-bottom: 4px solid #fc0;
border-left: 4px solid transparent;
border-right: 4px solid transparent;
display:inline-block;
}
}
.jamtrack-tracks {
padding-bottom:30px;
position:relative;
@include border_box_sizing;
width:55%;
//padding: 10px 0px;
.tracks-caption {
margin-top: 5px;
margin-bottom: 10px;
}
.track-instrument {
margin-top: 5px;
}
.instrument-image {
float: left;
margin-right: 2px;
}
.instrument-desc {
margin-top: 6px;
float: left;
margin-left: 10px;
}
.instrument-name {
color:white;
}
}
.detail-arrow {
position:absolute;
float: right;
margin-right: 10px;
bottom:14px;
left:12px;
}
.jamtrack-name {
font-size:16px;
white-space: normal;
}
.jamtrack-original-artist {
font-size:16px;
margin-top:10px;
margin-bottom:5px;
white-space: normal;
}
.jamtrack-track {
float:left;
padding:0 0 8px 7px;
width: 250px;
@include border_box_sizing;
&.hidden {
display:none;
}
}
.jam-track-preview {
font-size:11px;
white-space:nowrap;
.loading-text {
right:-115px;
background-color:#262626;
}
}
.jamtrack-action {
@include border_box_sizing;
width: 20%;
text-align: center;
vertical-align: middle;
.jamtrack-action-container {
display:inline-block;
}
.play-button {
margin-top: 5px;
}
.jamtrack-price {
margin-top: 5px;
font-size: 20px;
&.free {
margin-top:0;
display:none;
.free-state {
font-size: 11px;
margin-top: 5px;
display:block;
}
}
}
.jamtrack-add-cart, .jamtrack-add-cart-disabled {
margin: 8px 0px;
}
.jamtrack-license {
margin-left: 20%;
margin-right: 20%;
font-size: 13px;
width: 60%;
}
}
.end-of-jamtrack-list {
margin-top:20px;
}
}
#jamtrack-license-dialog {
.dialog-inner {
height: auto;
.content-body {
max-height: auto;
.content-body-scroller {
height: 350px;
.paragraph {
margin-bottom: 1em;
}
overflow: hidden;
}
border: 1px solid #222;
margin: 4px 4px 8px 4px;
}
}
}
.jamtrack_buttons {
margin: 8px 4px 12px 4px;
}

View File

@ -0,0 +1,10 @@
/*
*
*
# comes from the gem 'rails-assets-react-select'
*= require react-select/dist/default.scss
*/
@import "client/common.css.scss";
.Select-control {
background-color:$ColorTextBoxBackground;
}

View File

@ -5,33 +5,4 @@
h1 jamtracks
= render "screen_navigation"
.content-body
.content-body-scroller
.list-columns
.about
h2 what are jamtracks?
.what
.details JamTracks are the best way to play along with your favorite music! Unlike traditional backing tracks, JamTracks are professionally mastered, complete multitrack recordings, with fully isolated tracks for each and every part of the master mix. Used with the free JamKazam app & Internet service, you can:
ul
li Solo just the part you want to play in order to hear and learn it
li Mute just the part you want to play and play along with the rest
li Make audio recordings and share them via Facebook or URL
li Make video recordings and share them via YouTube
li And even go online to play with others in real time -- for example, you can play the electric guitar lead, while someone else plays the bass, and all other parts play from the recorded tracks in your session
a.video-thumbnail href="https://www.youtube.com/watch?v=askHvcCoNfw" rel="external"
img.play src="/assets/content/icon_youtube_play.png"
.browse
h2 my jamtracks
.howto
.no-free-jamtrack.details.hidden
span="To play with your JamTracks, open a JamTrack while in a session in the JamKazam app. Or "
a href="client#/account/jamtracks" visit the JamTracks Section of your account.
.free-jamtrack.orange-fill.details.hidden
| For a limited time, get one JamTrack free. Browse JamTracks below, add one to your shopping cart, and we'll make it free during the checkout process.
h2.browse-jamtracks browse jamtracks
.browse-header
| browse by band &nbsp;&nbsp;&nbsp;
a href="client#/jamtrackBrowse" or browse all jamtracks
.band-browse.two-column-list-container
ul#band_list
li#no_bands_found.hidden No bands found
= react_component 'JamTrackLandingScreen', {}

View File

@ -0,0 +1,8 @@
#jamtrackSearch.screen.secondary.no-login-required layout='screen' layout-id='jamtrack/search'
.content
.content-head
.content-icon=image_tag("content/icon_jamtracks.png", height:19, width:19)
h1 search jamtracks
= render "screen_navigation"
.content-body
= react_component 'JamTrackSearchScreen', {}

View File

@ -1,167 +0,0 @@
#session-screen-old.screen.secondary[layout="screen" layout-id="session_old" layout-arg="id"]
.content-head
.content-icon
= image_tag "shared/icon_session.png", {:height => 19, :width => 19}
h1
| session
.content-body
#session-controls
a#session-resync.button-grey.resync.left
= image_tag "content/icon_resync.png", {:align => "texttop", :height => 12, :width => 12}
| RESYNC
a#session-settings-button.button-grey.left[layout-link="session-settings"]
= image_tag "content/icon_settings_sm.png", {:align => "texttop", :height => 12, :width => 12}
| SETTINGS
a.button-grey.left[layout-link="share-dialog"]
= image_tag "content/icon_share.png", {:align => "texttop", :height => 12, :width => 12}
| SHARE
- if (Rails.application.config.video_available && Rails.application.config.video_available!="none")
a#session-webcam.button-grey-toggle.video.left
= image_tag "content/icon_cam.png", {:align => "texttop", :height => 12, :width => 12}
| VIDEO
.block
.label
| VOLUME:
#volume.fader.lohi[mixer-id=""]
.block.monitor-mode-holder
.label
| MIX:
select.monitor-mode.easydropdown
option.label[value="personal"]
| Personal
option[value="master"]
| Master
a#session-leave.button-grey.right.leave[href="/client#/home"]
| X  LEAVE
#tracks
.content-scroller
.content-wrapper
.session-mytracks
h2
| my tracks
#track-settings.session-add[style="display:block;" layout-link="configure-tracks"]
= image_tag "content/icon_settings_lg.png", {:width => 18, :height => 18}
span
| Settings
.session-tracks-scroller
#session-mytracks-notracks
p.notice
| You have not set up any inputs for your instrument or vocals.&nbsp;
| If you want to hear yourself play through the JamKazam app,&nbsp;
| and let the app mix your live playing with JamTracks, or with other musicians in online sessions,&nbsp;
a.open-ftue-no-tracks href='#' click here now.
#session-mytracks-container
#voice-chat.voicechat[style="display:none;" mixer-id=""]
.voicechat-label
| CHAT
.voicechat-gain
.voicechat-mute.enabled[control="mute" mixer-id=""]
.session-fluidtracks
.session-livetracks
h2
| live tracks
.session-add[layout-link="select-invites"]
a#session-invite-musicians[href="#"]
= image_tag "content/icon_add.png", {:width => 19, :height => 19, :align => "texttop"}
|   Invite Musicians
.session-tracks-scroller
#session-livetracks-container
.when-empty.livetracks
| No other musicians
br
| are in your session
br[clear="all"]
#recording-start-stop.recording
a
= image_tag "content/recordbutton-off.png", {:width => 20, :height => 20, :align => "absmiddle"}
|   
span#recording-status
| Make Recording
.session-recordings
h2
| other audio
.session-recording-name-wrapper
.session-recording-name.left
| (No audio loaded)
.session-add.right
a#close-playback-recording[href="#"]
= image_tag "content/icon_close.png", {:width => 18, :height => 20, :align => "texttop"}
|   Close
.session-tracks-scroller
#session-recordedtracks-container
.when-empty.recordings
span.open-media-file-header
= image_tag "content/icon_folder.png", {width:22, height:20}
| Open:
ul.open-media-file-options
li
a#open-a-recording[href="#"]
| Recording
- if Rails.application.config.jam_tracks_available || (current_user && current_user.admin)
li.open-a-jamtrack
a#open-a-jamtrack[href="#"]
| JamTrack
- if Rails.application.config.backing_tracks_available
li
a#open-a-backingtrack[href="#"]
| Audio File
.when-empty.use-metronome-header
- if Rails.application.config.metronome_available
= image_tag "content/icon_metronome.png", {width:22, height:20}
a#open-a-metronome[href="#"]
| Use Metronome
br[clear="all"]
.play-controls-holder
= render "play_controls"
.webcam-container.hidden
/ Webcam is actually in another window.
= render 'webcam'
= render "configureTrack"
= render "addTrack"
= render "addNewGear"
= render "error"
= render "sessionSettings"
script#template-session-track[type="text/template"]
.session-track.track client-id="{clientId}" track-id="{trackId}"
.track-vu-left.mixer-id="{vuMixerId}_vul"
.track-vu-right.mixer-id="{vuMixerId}_vur"
.track-label[title="{name}"]
span.name-text="{name}"
#div-track-close.track-close.op30 track-id="{trackId}"
=image_tag("content/icon_closetrack.png", {width: 12, height: 12})
div class="{avatarClass}"
img src="{avatar}"
.track-instrument class="{preMasteredClass}"
img height="45" src="{instrumentIcon}" width="45"
.track-gain mixer-id="{mixerId}"
.track-icon-mute class="{muteClass}" control="mute" mixer-id="{muteMixerId}"
.track-icon-loop.hidden control="loop"
input#loop-button type="checkbox" value="loop" Loop
.track-connection.grey mixer-id="{mixerId}_connection"
CONNECTION
.disabled-track-overlay
.metronome-selects.hidden
select.metronome-select.metro-sound title="Metronome Sound"
option.label value="Beep" Knock
option.label value="Click" Tap
option.label value="Snare" Snare
option.label value="Kick" Kick
br
select.metronome-select.metro-tempo title="Metronome Tempo"
- metronome_tempos.each do |t|
option.label value=t
=t
script#template-option type="text/template"
option value="{value}" title="{label}" selected="{selected}"
="{label}"
script#template-genre-option type="text/template"
option value="{value}"
="{label}"
script#template-pending-metronome type="text/template"
.pending-metronome
.spinner-large
p Your metronome is synchronizing.

View File

@ -6,3 +6,52 @@
| session
.content-body
= react_component 'SessionScreen', {}
= render "configureTrack"
= render "addTrack"
= render "addNewGear"
= render "error"
= render "sessionSettings"
script#template-session-track[type="text/template"]
.session-track.track client-id="{clientId}" track-id="{trackId}"
.track-vu-left.mixer-id="{vuMixerId}_vul"
.track-vu-right.mixer-id="{vuMixerId}_vur"
.track-label[title="{name}"]
span.name-text="{name}"
#div-track-close.track-close.op30 track-id="{trackId}"
=image_tag("content/icon_closetrack.png", {width: 12, height: 12})
div class="{avatarClass}"
img src="{avatar}"
.track-instrument class="{preMasteredClass}"
img height="45" src="{instrumentIcon}" width="45"
.track-gain mixer-id="{mixerId}"
.track-icon-mute class="{muteClass}" control="mute" mixer-id="{muteMixerId}"
.track-icon-loop.hidden control="loop"
input#loop-button type="checkbox" value="loop" Loop
.track-connection.grey mixer-id="{mixerId}_connection"
CONNECTION
.disabled-track-overlay
.metronome-selects.hidden
select.metronome-select.metro-sound title="Metronome Sound"
option.label value="Beep" Knock
option.label value="Click" Tap
option.label value="Snare" Snare
option.label value="Kick" Kick
br
select.metronome-select.metro-tempo title="Metronome Tempo"
- metronome_tempos.each do |t|
option.label value=t
=t
script#template-option type="text/template"
option value="{value}" title="{label}" selected="{selected}"
="{label}"
script#template-genre-option type="text/template"
option value="{value}"
="{label}"
script#template-pending-metronome type="text/template"
.pending-metronome
.spinner-large
p Your metronome is synchronizing.

View File

@ -31,7 +31,6 @@
<%= render "sidebar" %>
<%= render "scheduledSession" %>
<%= render "findSession" %>
<%= render "session" %>
<%= render "session2" %>
<%= render "profile" %>
<%= render "bandProfile" %>
@ -40,6 +39,7 @@
<%= render "users/feed_music_session_ajax" %>
<%= render "users/feed_recording_ajax" %>
<%= render "jamtrack_browse" %>
<%= render "jamtrack_search" %>
<%= render "jamtrack_landing" %>
<%= render "shopping_cart" %>
<%= render "checkout_signin" %>