codecombat/app/views/play/ladder/ladder_tab.coffee

347 lines
11 KiB
CoffeeScript
Raw Normal View History

2014-03-02 15:43:21 -05:00
CocoView = require 'views/kinds/CocoView'
CocoClass = require 'lib/CocoClass'
2014-03-02 15:43:21 -05:00
Level = require 'models/Level'
LevelSession = require 'models/LevelSession'
CocoCollection = require 'collections/CocoCollection'
User = require 'models/User'
2014-03-02 15:43:21 -05:00
LeaderboardCollection = require 'collections/LeaderboardCollection'
{teamDataFromLevel} = require './utils'
ModelModal = require 'views/modal/model_modal'
2014-03-02 15:43:21 -05:00
HIGHEST_SCORE = 1000000
class LevelSessionsCollection extends CocoCollection
url: ''
model: LevelSession
constructor: (levelID) ->
super()
@url = "/db/level/#{levelID}/all_sessions"
module.exports = class LadderTabView extends CocoView
2014-03-02 15:43:21 -05:00
id: 'ladder-tab-view'
template: require 'templates/play/ladder/ladder_tab'
events:
2014-03-22 00:11:51 -04:00
'click .connect-facebook': 'onConnectFacebook'
2014-03-26 17:19:05 -04:00
'click .connect-google-plus': 'onConnectGPlus'
'click .name-col-cell': 'onClickPlayerName'
'click .load-more-ladder-entries': 'onLoadMoreLadderEntries'
subscriptions:
2014-03-26 16:50:01 -04:00
'fbapi-loaded': 'checkFriends'
'gapi-loaded': 'checkFriends'
2014-03-22 00:11:51 -04:00
'facebook-logged-in': 'onConnectedWithFacebook'
2014-03-26 17:19:05 -04:00
'gplus-logged-in': 'onConnectedWithGPlus'
2014-03-02 15:43:21 -05:00
constructor: (options, @level, @sessions) ->
super(options)
2014-04-19 12:08:40 -04:00
@socialNetworkRes = @supermodel.addSomethingResource("social_network_apis", 0)
2014-03-02 15:43:21 -05:00
@teams = teamDataFromLevel @level
@leaderboards = {}
2014-03-03 15:21:59 -05:00
@refreshLadder()
@checkFriends()
checkFriends: ->
return if @checked or (not window.FB) or (not window.gapi)
2014-03-26 16:50:01 -04:00
@checked = true
2014-05-05 18:33:08 -04:00
# @addSomethingToLoad("facebook_status")
2014-04-19 12:08:40 -04:00
@fbStatusRes = @supermodel.addSomethingResource("facebook_status", 0)
@fbStatusRes.load()
FB.getLoginStatus (response) =>
return if @destroyed
@facebookStatus = response.status
@loadFacebookFriends() if @facebookStatus is 'connected'
@fbStatusRes.markLoaded()
if application.gplusHandler.loggedIn is undefined
@listenToOnce(application.gplusHandler, 'checked-state', @gplusSessionStateLoaded)
else
@gplusSessionStateLoaded()
@socialNetworkRes.markLoaded()
# FACEBOOK
onConnectFacebook: ->
@connecting = true
FB.login()
onConnectedWithFacebook: -> location.reload() if @connecting
loadFacebookFriends: ->
# @addSomethingToLoad("facebook_friends")
2014-04-19 12:08:40 -04:00
@fbFriendRes = @supermodel.addSomethingResource("facebook_friends", 0)
@fbFriendRes.load()
FB.api '/me/friends', @onFacebookFriendsLoaded
2014-05-05 18:33:08 -04:00
onFacebookFriendsLoaded: (response) =>
@facebookData = response.data
@loadFacebookFriendSessions()
@fbFriendRes.markLoaded()
loadFacebookFriendSessions: ->
levelFrag = "#{@level.get('original')}.#{@level.get('version').major}"
url = "/db/level/#{levelFrag}/leaderboard_facebook_friends"
@fbFriendSessionRes = @supermodel.addRequestResource('facebook_friend_sessions', {
url: url
data: { friendIDs: (f.id for f in @facebookData) }
method: 'POST'
success: @onFacebookFriendSessionsLoaded
})
@fbFriendSessionRes.load()
onFacebookFriendSessionsLoaded: (result) =>
friendsMap = {}
friendsMap[friend.id] = friend.name for friend in @facebookData
for friend in result
2014-03-26 16:50:01 -04:00
friend.name = friendsMap[friend.facebookID]
friend.otherTeam = if friend.team is 'humans' then 'ogres' else 'humans'
2014-03-26 16:50:01 -04:00
friend.imageSource = "http://graph.facebook.com/#{friend.facebookID}/picture"
@facebookFriendSessions = result
2014-05-05 18:33:08 -04:00
# GOOGLE PLUS
2014-03-26 17:19:05 -04:00
onConnectGPlus: ->
@connecting = true
@listenToOnce application.gplusHandler, 'logged-in', @onConnectedWithGPlus
application.gplusHandler.reauthorize()
onConnectedWithGPlus: -> location.reload() if @connecting
2014-05-05 18:33:08 -04:00
gplusSessionStateLoaded: ->
if application.gplusHandler.loggedIn
#@addSomethingToLoad("gplus_friends")
2014-04-19 12:08:40 -04:00
@gpFriendRes = @supermodel.addSomethingResource("gplus_friends", 0)
@gpFriendRes.load()
application.gplusHandler.loadFriends @gplusFriendsLoaded
gplusFriendsLoaded: (friends) =>
@gplusData = friends.items
@loadGPlusFriendSessions()
@gpFriendRes.markLoaded()
loadGPlusFriendSessions: ->
levelFrag = "#{@level.get('original')}.#{@level.get('version').major}"
url = "/db/level/#{levelFrag}/leaderboard_gplus_friends"
@gpFriendSessionRes = @supermodel.addRequestResource('gplus_friend_sessions', {
url: url
data: { friendIDs: (f.id for f in @gplusData) }
method: 'POST'
success: @onGPlusFriendSessionsLoaded
})
@gpFriendSessionRes.load()
onGPlusFriendSessionsLoaded: (result) =>
2014-03-26 16:50:01 -04:00
friendsMap = {}
friendsMap[friend.id] = friend for friend in @gplusData
for friend in result
friend.name = friendsMap[friend.gplusID].displayName
friend.otherTeam = if friend.team is 'humans' then 'ogres' else 'humans'
friend.imageSource = friendsMap[friend.gplusID].image.url
@gplusFriendSessions = result
2014-05-05 18:33:08 -04:00
# LADDER LOADING
2014-03-03 15:21:59 -05:00
refreshLadder: ->
@supermodel.resetProgress()
@ladderLimit ?= parseInt @getQueryVariable('top_players', 20)
2014-03-02 15:43:21 -05:00
for team in @teams
if oldLeaderboard = @leaderboards[team.id]
@supermodel.removeModelResource oldLeaderboard
oldLeaderboard.destroy()
teamSession = _.find @sessions.models, (session) -> session.get('team') is team.id
@leaderboards[team.id] = new LeaderboardData(@level, team.id, teamSession, @ladderLimit)
@leaderboardRes = @supermodel.addModelResource(@leaderboards[team.id], 'leaderboard', 3)
@leaderboardRes.load()
2014-03-02 15:43:21 -05:00
2014-04-04 16:38:36 -04:00
render: ->
super()
2014-05-05 18:33:08 -04:00
2014-04-04 16:38:36 -04:00
@$el.find('.histogram-display').each (i, el) =>
histogramWrapper = $(el)
team = _.find @teams, name: histogramWrapper.data('team-name')
histogramData = null
$.when(
$.get("/db/level/#{@level.get('slug')}/histogram_data?team=#{team.name.toLowerCase()}", (data) -> histogramData = data)
).then =>
2014-05-05 18:33:08 -04:00
@generateHistogram(histogramWrapper, histogramData, team.name.toLowerCase()) unless @destroyed
2014-03-02 15:43:21 -05:00
getRenderData: ->
ctx = super()
ctx.level = @level
ctx.link = "/play/level/#{@level.get('name')}"
ctx.teams = @teams
team.leaderboard = @leaderboards[team.id] for team in @teams
ctx.levelID = @levelID
2014-03-26 16:50:01 -04:00
ctx.friends = @consolidateFriends()
ctx.onFacebook = @facebookStatus is 'connected'
ctx.onGPlus = application.gplusHandler.loggedIn
2014-03-02 15:43:21 -05:00
ctx
2014-03-03 15:21:59 -05:00
2014-04-04 16:38:36 -04:00
generateHistogram: (histogramElement, histogramData, teamName) ->
#renders twice, hack fix
if $("#"+histogramElement.attr("id")).has("svg").length then return
histogramData = histogramData.map (d) -> d*100
2014-05-05 18:33:08 -04:00
2014-04-04 16:38:36 -04:00
margin =
top: 20
right: 20
bottom: 30
left: 0
width = 300 - margin.left - margin.right
height = 125 - margin.top - margin.bottom
2014-05-05 18:33:08 -04:00
2014-04-04 16:38:36 -04:00
formatCount = d3.format(",.0")
2014-05-05 18:33:08 -04:00
2014-04-04 16:38:36 -04:00
x = d3.scale.linear().domain([-3000,6000]).range([0,width])
data = d3.layout.histogram().bins(x.ticks(20))(histogramData)
y = d3.scale.linear().domain([0,d3.max(data, (d) -> d.y)]).range([height,10])
2014-05-05 18:33:08 -04:00
2014-04-04 16:38:36 -04:00
#create the x axis
xAxis = d3.svg.axis().scale(x).orient("bottom").ticks(5).outerTickSize(0)
2014-05-05 18:33:08 -04:00
2014-04-04 16:38:36 -04:00
svg = d3.select("#"+histogramElement.attr("id")).append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform","translate(#{margin.left},#{margin.top})")
2014-04-04 17:22:30 -04:00
barClass = "bar"
if teamName.toLowerCase() is "ogres" then barClass = "ogres-bar"
if teamName.toLowerCase() is "humans" then barClass = "humans-bar"
2014-05-05 18:33:08 -04:00
2014-04-04 16:38:36 -04:00
bar = svg.selectAll(".bar")
.data(data)
.enter().append("g")
2014-04-04 17:22:30 -04:00
.attr("class",barClass)
2014-05-05 18:33:08 -04:00
.attr("transform", (d) -> "translate(#{x(d.x)},#{y(d.y)})")
2014-04-04 16:38:36 -04:00
bar.append("rect")
.attr("x",1)
.attr("width",width/20)
.attr("height", (d) -> height - y(d.y))
if @leaderboards[teamName].session?
playerScore = @leaderboards[teamName].session.get('totalScore') * 100
scorebar = svg.selectAll(".specialbar")
.data([playerScore])
.enter().append("g")
.attr("class","specialbar")
.attr("transform", "translate(#{x(playerScore)},#{y(9001)})")
2014-05-05 18:33:08 -04:00
2014-04-04 16:38:36 -04:00
scorebar.append("rect")
.attr("x",1)
.attr("width",3)
.attr("height",height - y(9001))
2014-04-04 17:55:55 -04:00
rankClass = "rank-text"
if teamName.toLowerCase() is "ogres" then rankClass = "rank-text ogres-rank-text"
if teamName.toLowerCase() is "humans" then rankClass = "rank-text humans-rank-text"
2014-05-05 18:33:08 -04:00
2014-04-04 17:55:55 -04:00
message = "#{histogramData.length} players"
if @leaderboards[teamName].session?
2014-05-16 16:30:55 -04:00
if @leaderboards[teamName].myRank <= histogramData.length
message="##{@leaderboards[teamName].myRank} of #{histogramData.length}"
else
message="Rank your session!"
2014-04-04 17:55:55 -04:00
svg.append("g")
.append("text")
.attr("class",rankClass)
.attr("y",0)
.attr("text-anchor","end")
.attr("x",width)
.text(message)
2014-05-05 18:33:08 -04:00
2014-04-04 16:38:36 -04:00
#Translate the x-axis up
svg.append("g")
.attr("class", "x axis")
.attr("transform","translate(0," + height + ")")
.call(xAxis)
2014-05-05 18:33:08 -04:00
2014-03-26 16:50:01 -04:00
consolidateFriends: ->
allFriendSessions = (@facebookFriendSessions or []).concat(@gplusFriendSessions or [])
2014-03-26 17:22:04 -04:00
sessions = _.uniq allFriendSessions, false, (session) -> session._id
sessions = _.sortBy sessions, 'totalScore'
sessions.reverse()
sessions
2014-03-26 16:50:01 -04:00
# Admin view of players' code
onClickPlayerName: (e) ->
return unless me.isAdmin()
row = $(e.target).parent()
player = new User _id: row.data 'player-id'
session = new LevelSession _id: row.data 'session-id'
@openModalView new ModelModal models: [session, player]
onLoadMoreLadderEntries: (e) ->
@ladderLimit ?= 100
@ladderLimit += 100
@refreshLadder()
class LeaderboardData extends CocoClass
###
Consolidates what you need to load for a leaderboard into a single Backbone Model-like object.
###
2014-04-13 17:48:36 -04:00
constructor: (@level, @team, @session, @limit) ->
super()
@fetch()
fetch: ->
@topPlayers = new LeaderboardCollection(@level, {order:-1, scoreOffset: HIGHEST_SCORE, team: @team, limit: @limit})
promises = []
promises.push @topPlayers.fetch()
2014-03-02 15:43:21 -05:00
if @session
score = @session.get('totalScore') or 10
@playersAbove = new LeaderboardCollection(@level, {order:1, scoreOffset: score, limit: 4, team: @team})
promises.push @playersAbove.fetch()
@playersBelow = new LeaderboardCollection(@level, {order:-1, scoreOffset: score, limit: 4, team: @team})
promises.push @playersBelow.fetch()
level = "#{@level.get('original')}.#{@level.get('version').major}"
success = (@myRank) =>
promises.push $.ajax "/db/level/#{level}/leaderboard_rank?scoreOffset=#{@session.get('totalScore')}&team=#{@team}", {success}
@promise = $.when(promises...)
@promise.then @onLoad
@promise.fail @onFail
@promise
onLoad: =>
return if @destroyed
2014-03-09 16:22:22 -04:00
@loaded = true
@trigger 'sync', @
# TODO: cache user ids -> names mapping, and load them here as needed,
# and apply them to sessions. Fetching each and every time is too costly.
2014-05-05 18:33:08 -04:00
onFail: (resource, jqxhr) =>
return if @destroyed
@trigger 'error', @, jqxhr
2014-03-21 11:09:08 -04:00
inTopSessions: ->
return me.id in (session.attributes.creator for session in @topPlayers.models)
2014-03-21 11:09:08 -04:00
nearbySessions: ->
2014-03-24 17:38:18 -04:00
return [] unless @session?.get('totalScore')
l = []
above = @playersAbove.models
l = l.concat(above)
l.reverse()
l.push @session
l = l.concat(@playersBelow.models)
if @myRank
startRank = @myRank - 4
session.rank = startRank + i for session, i in l
2014-03-21 11:09:08 -04:00
l
allResources: ->
resources = [@topPlayers, @playersAbove, @playersBelow]
return (r for r in resources when r)