mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-03-02 08:24:07 -05:00
Update private clans dashboard
This commit is contained in:
parent
2f8831ea72
commit
45c070209b
4 changed files with 142 additions and 46 deletions
app
server/clans
|
@ -25,9 +25,39 @@
|
||||||
|
|
||||||
$spriteSheetSize: 30px
|
$spriteSheetSize: 30px
|
||||||
|
|
||||||
td.hero-icon-cell
|
|
||||||
|
.remove-hero-cell
|
||||||
|
width: 100px
|
||||||
|
|
||||||
|
.hero-icon-cell
|
||||||
width: 30px
|
width: 30px
|
||||||
|
|
||||||
|
.level-cell
|
||||||
|
width: 50px
|
||||||
|
text-align: center
|
||||||
|
|
||||||
|
.name-cell
|
||||||
|
width: 100px
|
||||||
|
|
||||||
|
.level-progression-cell
|
||||||
|
background-color: lightblue
|
||||||
|
border: 1px solid gray
|
||||||
|
// cursor: pointer
|
||||||
|
padding: 4px
|
||||||
|
|
||||||
|
.level-popup-container
|
||||||
|
display: none
|
||||||
|
position: absolute
|
||||||
|
padding: 10px
|
||||||
|
border: 1px solid black
|
||||||
|
z-index: 3
|
||||||
|
background-color: blanchedalmond
|
||||||
|
font-size: 10pt
|
||||||
|
|
||||||
|
.level-progression-cell-name
|
||||||
|
background-color: lightblue
|
||||||
|
border: 1px solid gray
|
||||||
|
|
||||||
.player-hero-icon
|
.player-hero-icon
|
||||||
background: transparent url(/images/pages/play/play-spritesheet.png)
|
background: transparent url(/images/pages/play/play-spritesheet.png)
|
||||||
background-size: cover
|
background-size: cover
|
||||||
|
|
|
@ -54,7 +54,7 @@ block content
|
||||||
tr
|
tr
|
||||||
td Average Level
|
td Average Level
|
||||||
td= stats.averageLevel
|
td= stats.averageLevel
|
||||||
if stats.totalAchievements
|
if stats.totalAchievements && clan.get('type') === 'public'
|
||||||
tr
|
tr
|
||||||
td Total Achievements
|
td Total Achievements
|
||||||
td= stats.totalAchievements
|
td= stats.totalAchievements
|
||||||
|
@ -67,40 +67,78 @@ block content
|
||||||
else
|
else
|
||||||
button.btn.btn-lg.btn-success.join-clan-btn Join Clan
|
button.btn.btn-lg.btn-success.join-clan-btn Join Clan
|
||||||
|
|
||||||
div
|
if clan.get('ownerID') === me.id || clan.get('type') === 'public'
|
||||||
span.spl.spr.join-link-prompt Invite:
|
div
|
||||||
input.join-clan-link(type="text", readonly, value="#{joinClanLink}")
|
span.spl.spr.join-link-prompt Invite:
|
||||||
.small *Invite players to this Clan by sending them this link.
|
input.join-clan-link(type="text", readonly, value="#{joinClanLink}")
|
||||||
|
.small *Invite players to this Clan by sending them this link.
|
||||||
|
|
||||||
if members
|
if members
|
||||||
h3 Heroes (#{members.length})
|
h3 Heroes (#{members.length})
|
||||||
table.table.table-striped.table-condensed
|
if clan.get('type') === 'private'
|
||||||
thead
|
table.table.table-condensed
|
||||||
tr
|
thead
|
||||||
th
|
|
||||||
th
|
|
||||||
td Name
|
|
||||||
th Level
|
|
||||||
th Achievements
|
|
||||||
th Latest Achievement
|
|
||||||
th
|
|
||||||
tbody
|
|
||||||
each member in members
|
|
||||||
tr
|
tr
|
||||||
td.hero-icon-cell
|
if isOwner
|
||||||
span.spr.player-hero-icon(data-memberid="#{member.id}")
|
th
|
||||||
td.code-language-cell
|
th
|
||||||
if memberLanguageMap && memberLanguageMap[member.id]
|
th
|
||||||
span.code-language-cell(style="background-image: url(/images/common/code_languages/#{memberLanguageMap[member.id]}_small.png)", title=memberLanguageMap[member.id])
|
th Level
|
||||||
td
|
th Name
|
||||||
a(href="/user/#{member.id}")= member.get('name') || 'Anoner'
|
th(colspan="#{memberMaxLevelCount + 1}") Last Level Completed
|
||||||
td= member.level()
|
tbody
|
||||||
td
|
each member in members
|
||||||
if memberAchievementsMap && memberAchievementsMap[member.id]
|
tr
|
||||||
| #{memberAchievementsMap[member.id].length}
|
if isOwner
|
||||||
td
|
td.remove-hero-cell
|
||||||
if memberAchievementsMap && memberAchievementsMap[member.id] && memberAchievementsMap[member.id].length
|
if member.id !== clan.get('ownerID')
|
||||||
span= memberAchievementsMap[member.id][0].get('achievementName')
|
button.btn.btn-xs.btn-warning.remove-member-btn(data-id="#{member.id}") Remove Hero
|
||||||
td
|
td.hero-icon-cell
|
||||||
if isOwner && member.id !== clan.get('ownerID')
|
span.spr.player-hero-icon(data-memberid="#{member.id}")
|
||||||
button.btn.btn-xs.btn-warning.remove-member-btn(data-id="#{member.id}") Remove Hero
|
td.code-language-cell
|
||||||
|
if memberLanguageMap && memberLanguageMap[member.id]
|
||||||
|
span.code-language-cell(style="background-image: url(/images/common/code_languages/#{memberLanguageMap[member.id]}_small.png)", title=memberLanguageMap[member.id])
|
||||||
|
td.level-cell= member.level()
|
||||||
|
td.name-cell
|
||||||
|
a(href="/user/#{member.id}")= member.get('name') || 'Anoner'
|
||||||
|
if memberLevelProgression && memberLevelProgression[member.id]
|
||||||
|
each levelInfo in memberLevelProgression[member.id]
|
||||||
|
td.level-progression-cell
|
||||||
|
.level-popup-container
|
||||||
|
div Level: #{levelInfo.level}
|
||||||
|
div Playtime: #{levelInfo.playtime}
|
||||||
|
div Last played: #{levelInfo.changed}
|
||||||
|
td(colspan="#{memberMaxLevelCount - memberLevelProgression[member.id].length + 1}")= memberLevelProgression[member.id][memberLevelProgression[member.id].length - 1].level
|
||||||
|
else
|
||||||
|
td(colspan="#{memberMaxLevelCount + 1}")
|
||||||
|
else
|
||||||
|
table.table.table-striped.table-condensed
|
||||||
|
thead
|
||||||
|
tr
|
||||||
|
th
|
||||||
|
th
|
||||||
|
td Name
|
||||||
|
th Level
|
||||||
|
th Achievements
|
||||||
|
th Latest Achievement
|
||||||
|
th
|
||||||
|
tbody
|
||||||
|
each member in members
|
||||||
|
tr
|
||||||
|
td.hero-icon-cell
|
||||||
|
span.spr.player-hero-icon(data-memberid="#{member.id}")
|
||||||
|
td.code-language-cell
|
||||||
|
if memberLanguageMap && memberLanguageMap[member.id]
|
||||||
|
span.code-language-cell(style="background-image: url(/images/common/code_languages/#{memberLanguageMap[member.id]}_small.png)", title=memberLanguageMap[member.id])
|
||||||
|
td
|
||||||
|
a(href="/user/#{member.id}")= member.get('name') || 'Anoner'
|
||||||
|
td= member.level()
|
||||||
|
td
|
||||||
|
if memberAchievementsMap && memberAchievementsMap[member.id]
|
||||||
|
| #{memberAchievementsMap[member.id].length}
|
||||||
|
td
|
||||||
|
if memberAchievementsMap && memberAchievementsMap[member.id] && memberAchievementsMap[member.id].length
|
||||||
|
span= memberAchievementsMap[member.id][0].get('achievementName')
|
||||||
|
td
|
||||||
|
if isOwner && member.id !== clan.get('ownerID')
|
||||||
|
button.btn.btn-xs.btn-warning.remove-member-btn(data-id="#{member.id}") Remove Hero
|
||||||
|
|
|
@ -24,6 +24,8 @@ module.exports = class ClanDetailsView extends RootView
|
||||||
'click .join-clan-btn': 'onJoinClan'
|
'click .join-clan-btn': 'onJoinClan'
|
||||||
'click .leave-clan-btn': 'onLeaveClan'
|
'click .leave-clan-btn': 'onLeaveClan'
|
||||||
'click .remove-member-btn': 'onRemoveMember'
|
'click .remove-member-btn': 'onRemoveMember'
|
||||||
|
'mouseenter .level-progression-cell': 'onMouseEnterPoint'
|
||||||
|
'mouseleave .level-progression-cell': 'onMouseLeavePoint'
|
||||||
|
|
||||||
constructor: (options, @clanID) ->
|
constructor: (options, @clanID) ->
|
||||||
super options
|
super options
|
||||||
|
@ -61,6 +63,8 @@ module.exports = class ClanDetailsView extends RootView
|
||||||
context.owner = @owner
|
context.owner = @owner
|
||||||
context.memberAchievementsMap = @memberAchievementsMap
|
context.memberAchievementsMap = @memberAchievementsMap
|
||||||
context.memberLanguageMap = @memberLanguageMap
|
context.memberLanguageMap = @memberLanguageMap
|
||||||
|
context.memberLevelProgression = @memberLevelProgression
|
||||||
|
context.memberMaxLevelCount = @memberMaxLevelCount
|
||||||
context.members = @members?.models
|
context.members = @members?.models
|
||||||
context.isOwner = @clan.get('ownerID') is me.id
|
context.isOwner = @clan.get('ownerID') is me.id
|
||||||
context.isMember = @clanID in (me.get('clans') ? [])
|
context.isMember = @clanID in (me.get('clans') ? [])
|
||||||
|
@ -75,6 +79,7 @@ module.exports = class ClanDetailsView extends RootView
|
||||||
me.fetch cache: false
|
me.fetch cache: false
|
||||||
@members.fetch cache: false
|
@members.fetch cache: false
|
||||||
@memberAchievements.fetch cache: false
|
@memberAchievements.fetch cache: false
|
||||||
|
@memberSessions.fetch cache: false
|
||||||
|
|
||||||
updateHeroIcons: ->
|
updateHeroIcons: ->
|
||||||
return unless @members?.models?
|
return unless @members?.models?
|
||||||
|
@ -106,17 +111,27 @@ module.exports = class ClanDetailsView extends RootView
|
||||||
@render?()
|
@render?()
|
||||||
|
|
||||||
onMemberSessionsSync: ->
|
onMemberSessionsSync: ->
|
||||||
@memberSessionMap = {}
|
@memberLevelProgression = {}
|
||||||
|
memberSessions = {}
|
||||||
for levelSession in @memberSessions.models
|
for levelSession in @memberSessions.models
|
||||||
user = levelSession.get('creator')
|
user = levelSession.get('creator')
|
||||||
@memberSessionMap[user] ?= []
|
if not levelSession.isMultiplayer() and levelSession.get('state')?.complete is true
|
||||||
@memberSessionMap[user].push levelSession
|
memberSessions[user] ?= []
|
||||||
|
memberSessions[user].push levelSession
|
||||||
|
@memberLevelProgression[user] ?= []
|
||||||
|
levelInfo =
|
||||||
|
level: levelSession.get('levelName')
|
||||||
|
changed: new Date(levelSession.get('changed')).toLocaleString()
|
||||||
|
playtime: levelSession.get('playtime')
|
||||||
|
@memberLevelProgression[user].push levelInfo
|
||||||
|
@memberMaxLevelCount = 0
|
||||||
@memberLanguageMap = {}
|
@memberLanguageMap = {}
|
||||||
for user of @memberSessionMap
|
for user of memberSessions
|
||||||
languageCounts = {}
|
languageCounts = {}
|
||||||
for levelSession in @memberSessionMap[user]
|
for levelSession in memberSessions[user]
|
||||||
language = levelSession.get('codeLanguage') or levelSession.get('submittedCodeLanguage')
|
language = levelSession.get('codeLanguage') or levelSession.get('submittedCodeLanguage')
|
||||||
languageCounts[language] = (languageCounts[language] or 0) + 1 if language
|
languageCounts[language] = (languageCounts[language] or 0) + 1 if language
|
||||||
|
@memberMaxLevelCount = memberSessions[user].length if @memberMaxLevelCount < memberSessions[user].length
|
||||||
mostUsedCount = 0
|
mostUsedCount = 0
|
||||||
for language, count of languageCounts
|
for language, count of languageCounts
|
||||||
if count > mostUsedCount
|
if count > mostUsedCount
|
||||||
|
@ -124,6 +139,18 @@ module.exports = class ClanDetailsView extends RootView
|
||||||
@memberLanguageMap[user] = language
|
@memberLanguageMap[user] = language
|
||||||
@render?()
|
@render?()
|
||||||
|
|
||||||
|
onMouseEnterPoint: (e) ->
|
||||||
|
container = $(e.target).find('.level-popup-container').show()
|
||||||
|
margin = 20
|
||||||
|
offset = $(e.target).offset()
|
||||||
|
scrollTop = $(e.target).offsetParent().scrollTop()
|
||||||
|
height = container.outerHeight()
|
||||||
|
container.css('left', offset.left + e.offsetX)
|
||||||
|
container.css('top', offset.top + scrollTop - height - margin)
|
||||||
|
|
||||||
|
onMouseLeavePoint: (e) ->
|
||||||
|
$(e.target).find('.level-popup-container').hide()
|
||||||
|
|
||||||
onDeleteClan: (e) ->
|
onDeleteClan: (e) ->
|
||||||
return @openModalView(new AuthModal()) if me.isAnonymous()
|
return @openModalView(new AuthModal()) if me.isAnonymous()
|
||||||
options =
|
options =
|
||||||
|
|
|
@ -68,7 +68,7 @@ ClanHandler = class ClanHandler extends Handler
|
||||||
return @sendNotFoundError(res, err)
|
return @sendNotFoundError(res, err)
|
||||||
Clan.findById clanID, (err, clan) =>
|
Clan.findById clanID, (err, clan) =>
|
||||||
return @sendDatabaseError(res, err) if err
|
return @sendDatabaseError(res, err) if err
|
||||||
return @sendDatabaseError(res, err) unless clan
|
return @sendNotFoundError(res) unless clan
|
||||||
return @sendDatabaseError(res, err) unless clanType = clan.get('type')
|
return @sendDatabaseError(res, err) unless clanType = clan.get('type')
|
||||||
return @sendForbiddenError(res) unless clanType is 'public' or req.user.isPremium()
|
return @sendForbiddenError(res) unless clanType is 'public' or req.user.isPremium()
|
||||||
Clan.update {_id: clanID}, {$addToSet: {members: req.user._id}}, (err) =>
|
Clan.update {_id: clanID}, {$addToSet: {members: req.user._id}}, (err) =>
|
||||||
|
@ -86,7 +86,7 @@ ClanHandler = class ClanHandler extends Handler
|
||||||
return @sendNotFoundError(res, err)
|
return @sendNotFoundError(res, err)
|
||||||
Clan.findById clanID, (err, clan) =>
|
Clan.findById clanID, (err, clan) =>
|
||||||
return @sendDatabaseError(res, err) if err
|
return @sendDatabaseError(res, err) if err
|
||||||
return @sendDatabaseError(res, err) unless clan
|
return @sendNotFoundError(res) unless clan
|
||||||
return @sendForbiddenError(res) if clan.get('ownerID')?.equals req.user._id
|
return @sendForbiddenError(res) if clan.get('ownerID')?.equals req.user._id
|
||||||
Clan.update {_id: clanID}, {$pull: {members: req.user._id}}, (err) =>
|
Clan.update {_id: clanID}, {$pull: {members: req.user._id}}, (err) =>
|
||||||
return @sendDatabaseError(res, err) if err
|
return @sendDatabaseError(res, err) if err
|
||||||
|
@ -99,7 +99,7 @@ ClanHandler = class ClanHandler extends Handler
|
||||||
# TODO: add tests
|
# TODO: add tests
|
||||||
Clan.findById clanID, (err, clan) =>
|
Clan.findById clanID, (err, clan) =>
|
||||||
return @sendDatabaseError(res, err) if err
|
return @sendDatabaseError(res, err) if err
|
||||||
return @sendDatabaseError(res, err) unless clan
|
return @sendNotFoundError(res) unless clan
|
||||||
memberIDs = _.map clan.get('members') ? [], (memberID) -> memberID.toHexString?() or memberID
|
memberIDs = _.map clan.get('members') ? [], (memberID) -> memberID.toHexString?() or memberID
|
||||||
EarnedAchievement.find {user: {$in: memberIDs}}, (err, documents) =>
|
EarnedAchievement.find {user: {$in: memberIDs}}, (err, documents) =>
|
||||||
return @sendDatabaseError(res, err) if err?
|
return @sendDatabaseError(res, err) if err?
|
||||||
|
@ -110,7 +110,7 @@ ClanHandler = class ClanHandler extends Handler
|
||||||
# TODO: add tests
|
# TODO: add tests
|
||||||
Clan.findById clanID, (err, clan) =>
|
Clan.findById clanID, (err, clan) =>
|
||||||
return @sendDatabaseError(res, err) if err
|
return @sendDatabaseError(res, err) if err
|
||||||
return @sendDatabaseError(res, err) unless clan
|
return @sendNotFoundError(res) unless clan
|
||||||
memberIDs = clan.get('members') ? []
|
memberIDs = clan.get('members') ? []
|
||||||
User.find {_id: {$in: memberIDs}}, (err, users) =>
|
User.find {_id: {$in: memberIDs}}, (err, users) =>
|
||||||
return @sendDatabaseError(res, err) if err
|
return @sendDatabaseError(res, err) if err
|
||||||
|
@ -119,9 +119,10 @@ ClanHandler = class ClanHandler extends Handler
|
||||||
|
|
||||||
getMemberSessions: (req, res, clanID) ->
|
getMemberSessions: (req, res, clanID) ->
|
||||||
# TODO: add tests
|
# TODO: add tests
|
||||||
|
# TODO: restrict information returned based on clan type
|
||||||
Clan.findById clanID, (err, clan) =>
|
Clan.findById clanID, (err, clan) =>
|
||||||
return @sendDatabaseError(res, err) if err
|
return @sendDatabaseError(res, err) if err
|
||||||
return @sendDatabaseError(res, err) unless clan
|
return @sendNotFoundError(res) unless clan
|
||||||
memberIDs = _.map clan.get('members') ? [], (memberID) -> memberID.toHexString?() or memberID
|
memberIDs = _.map clan.get('members') ? [], (memberID) -> memberID.toHexString?() or memberID
|
||||||
LevelSession.find {creator: {$in: memberIDs}}, (err, documents) =>
|
LevelSession.find {creator: {$in: memberIDs}}, (err, documents) =>
|
||||||
return @sendDatabaseError(res, err) if err?
|
return @sendDatabaseError(res, err) if err?
|
||||||
|
@ -147,7 +148,7 @@ ClanHandler = class ClanHandler extends Handler
|
||||||
return @sendNotFoundError(res, err)
|
return @sendNotFoundError(res, err)
|
||||||
Clan.findById clanID, (err, clan) =>
|
Clan.findById clanID, (err, clan) =>
|
||||||
return @sendDatabaseError(res, err) if err
|
return @sendDatabaseError(res, err) if err
|
||||||
return @sendDatabaseError(res, err) unless clan
|
return @sendNotFoundError(res) unless clan
|
||||||
return @sendForbiddenError res unless @hasAccessToDocument(req, clan)
|
return @sendForbiddenError res unless @hasAccessToDocument(req, clan)
|
||||||
return @sendForbiddenError(res) if clan.get('ownerID').equals memberID
|
return @sendForbiddenError(res) if clan.get('ownerID').equals memberID
|
||||||
Clan.update {_id: clanID}, {$pull: {members: memberID}}, (err) =>
|
Clan.update {_id: clanID}, {$pull: {members: memberID}}, (err) =>
|
||||||
|
|
Loading…
Reference in a new issue