From 45c070209b734650ee04c9ccaebc422edb835249 Mon Sep 17 00:00:00 2001 From: Matt Lott <mattlott@live.com> Date: Thu, 16 Apr 2015 15:26:14 -0700 Subject: [PATCH] Update private clans dashboard --- app/styles/clans/clan-details.sass | 32 +++++++- app/templates/clans/clan-details.jade | 106 +++++++++++++++++-------- app/views/clans/ClanDetailsView.coffee | 37 +++++++-- server/clans/clan_handler.coffee | 13 +-- 4 files changed, 142 insertions(+), 46 deletions(-) diff --git a/app/styles/clans/clan-details.sass b/app/styles/clans/clan-details.sass index 2f092b68a..fc50e7559 100644 --- a/app/styles/clans/clan-details.sass +++ b/app/styles/clans/clan-details.sass @@ -25,9 +25,39 @@ $spriteSheetSize: 30px - td.hero-icon-cell + + .remove-hero-cell + width: 100px + + .hero-icon-cell 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 background: transparent url(/images/pages/play/play-spritesheet.png) background-size: cover diff --git a/app/templates/clans/clan-details.jade b/app/templates/clans/clan-details.jade index f642e3bcf..2775edcc5 100644 --- a/app/templates/clans/clan-details.jade +++ b/app/templates/clans/clan-details.jade @@ -54,7 +54,7 @@ block content tr td Average Level td= stats.averageLevel - if stats.totalAchievements + if stats.totalAchievements && clan.get('type') === 'public' tr td Total Achievements td= stats.totalAchievements @@ -67,40 +67,78 @@ block content else button.btn.btn-lg.btn-success.join-clan-btn Join Clan - div - span.spl.spr.join-link-prompt Invite: - input.join-clan-link(type="text", readonly, value="#{joinClanLink}") - .small *Invite players to this Clan by sending them this link. + if clan.get('ownerID') === me.id || clan.get('type') === 'public' + div + span.spl.spr.join-link-prompt Invite: + input.join-clan-link(type="text", readonly, value="#{joinClanLink}") + .small *Invite players to this Clan by sending them this link. if members h3 Heroes (#{members.length}) - 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 + if clan.get('type') === 'private' + table.table.table-condensed + thead 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 + if isOwner + th + th + th + th Level + th Name + th(colspan="#{memberMaxLevelCount + 1}") Last Level Completed + tbody + each member in members + tr + if isOwner + td.remove-hero-cell + if member.id !== clan.get('ownerID') + button.btn.btn-xs.btn-warning.remove-member-btn(data-id="#{member.id}") Remove Hero + 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.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 diff --git a/app/views/clans/ClanDetailsView.coffee b/app/views/clans/ClanDetailsView.coffee index 894214b42..4a039c431 100644 --- a/app/views/clans/ClanDetailsView.coffee +++ b/app/views/clans/ClanDetailsView.coffee @@ -24,6 +24,8 @@ module.exports = class ClanDetailsView extends RootView 'click .join-clan-btn': 'onJoinClan' 'click .leave-clan-btn': 'onLeaveClan' 'click .remove-member-btn': 'onRemoveMember' + 'mouseenter .level-progression-cell': 'onMouseEnterPoint' + 'mouseleave .level-progression-cell': 'onMouseLeavePoint' constructor: (options, @clanID) -> super options @@ -61,6 +63,8 @@ module.exports = class ClanDetailsView extends RootView context.owner = @owner context.memberAchievementsMap = @memberAchievementsMap context.memberLanguageMap = @memberLanguageMap + context.memberLevelProgression = @memberLevelProgression + context.memberMaxLevelCount = @memberMaxLevelCount context.members = @members?.models context.isOwner = @clan.get('ownerID') is me.id context.isMember = @clanID in (me.get('clans') ? []) @@ -75,6 +79,7 @@ module.exports = class ClanDetailsView extends RootView me.fetch cache: false @members.fetch cache: false @memberAchievements.fetch cache: false + @memberSessions.fetch cache: false updateHeroIcons: -> return unless @members?.models? @@ -106,17 +111,27 @@ module.exports = class ClanDetailsView extends RootView @render?() onMemberSessionsSync: -> - @memberSessionMap = {} + @memberLevelProgression = {} + memberSessions = {} for levelSession in @memberSessions.models user = levelSession.get('creator') - @memberSessionMap[user] ?= [] - @memberSessionMap[user].push levelSession + if not levelSession.isMultiplayer() and levelSession.get('state')?.complete is true + 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 = {} - for user of @memberSessionMap + for user of memberSessions languageCounts = {} - for levelSession in @memberSessionMap[user] + for levelSession in memberSessions[user] language = levelSession.get('codeLanguage') or levelSession.get('submittedCodeLanguage') languageCounts[language] = (languageCounts[language] or 0) + 1 if language + @memberMaxLevelCount = memberSessions[user].length if @memberMaxLevelCount < memberSessions[user].length mostUsedCount = 0 for language, count of languageCounts if count > mostUsedCount @@ -124,6 +139,18 @@ module.exports = class ClanDetailsView extends RootView @memberLanguageMap[user] = language @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) -> return @openModalView(new AuthModal()) if me.isAnonymous() options = diff --git a/server/clans/clan_handler.coffee b/server/clans/clan_handler.coffee index e52717a53..80d521a55 100644 --- a/server/clans/clan_handler.coffee +++ b/server/clans/clan_handler.coffee @@ -68,7 +68,7 @@ ClanHandler = class ClanHandler extends Handler return @sendNotFoundError(res, err) Clan.findById clanID, (err, clan) => 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 @sendForbiddenError(res) unless clanType is 'public' or req.user.isPremium() Clan.update {_id: clanID}, {$addToSet: {members: req.user._id}}, (err) => @@ -86,7 +86,7 @@ ClanHandler = class ClanHandler extends Handler return @sendNotFoundError(res, err) Clan.findById clanID, (err, clan) => 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 Clan.update {_id: clanID}, {$pull: {members: req.user._id}}, (err) => return @sendDatabaseError(res, err) if err @@ -99,7 +99,7 @@ ClanHandler = class ClanHandler extends Handler # TODO: add tests Clan.findById clanID, (err, clan) => 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 EarnedAchievement.find {user: {$in: memberIDs}}, (err, documents) => return @sendDatabaseError(res, err) if err? @@ -110,7 +110,7 @@ ClanHandler = class ClanHandler extends Handler # TODO: add tests Clan.findById clanID, (err, clan) => return @sendDatabaseError(res, err) if err - return @sendDatabaseError(res, err) unless clan + return @sendNotFoundError(res) unless clan memberIDs = clan.get('members') ? [] User.find {_id: {$in: memberIDs}}, (err, users) => return @sendDatabaseError(res, err) if err @@ -119,9 +119,10 @@ ClanHandler = class ClanHandler extends Handler getMemberSessions: (req, res, clanID) -> # TODO: add tests + # TODO: restrict information returned based on clan type Clan.findById clanID, (err, clan) => 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 LevelSession.find {creator: {$in: memberIDs}}, (err, documents) => return @sendDatabaseError(res, err) if err? @@ -147,7 +148,7 @@ ClanHandler = class ClanHandler extends Handler return @sendNotFoundError(res, err) Clan.findById clanID, (err, clan) => 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) if clan.get('ownerID').equals memberID Clan.update {_id: clanID}, {$pull: {members: memberID}}, (err) =>