mirror of
https://github.com/sudoxnym/habitica.git
synced 2026-08-04 01:29:21 +00:00
Merge branch 'develop' of https://github.com/lefnire/habitrpg into develop
This commit is contained in:
commit
5d160c7f21
25 changed files with 283 additions and 312 deletions
|
|
@ -38,13 +38,13 @@ module.exports = function(grunt) {
|
|||
'public/js/services/sharedServices.js',
|
||||
'public/js/services/userServices.js',
|
||||
'public/js/services/groupServices.js',
|
||||
'public/js/services/memberServices.js',
|
||||
|
||||
'public/js/filters/filters.js',
|
||||
|
||||
'public/js/directives/directives.js',
|
||||
|
||||
'public/js/controllers/authCtrl.js',
|
||||
'public/js/controllers/characterCtrl.js',
|
||||
'public/js/controllers/menuCtrl.js',
|
||||
'public/js/controllers/notificationCtrl.js',
|
||||
'public/js/controllers/rootCtrl.js',
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
"NODE_DB_URI":"mongodb://localhost/habitrpg",
|
||||
"NODE_ENV":"development",
|
||||
"SESSION_SECRET":"YOUR SECRET HERE",
|
||||
"ADMIN_EMAIL": "you@yours.com",
|
||||
"SMTP_USER":"user@domain.com",
|
||||
"SMTP_PASS":"password",
|
||||
"SMTP_SERVICE":"Gmail",
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@
|
|||
},
|
||||
"scripts": {
|
||||
"test": "mocha test/api.mocha.coffee",
|
||||
"start": "grunt run:dev",
|
||||
"postinstall": "./node_modules/bower/bin/bower install -f"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,13 +27,13 @@
|
|||
margin-right:5px
|
||||
|
||||
.label-elite
|
||||
background-color: #5DF644
|
||||
background-color: #077409
|
||||
color: black
|
||||
.label-champion
|
||||
background-color: #0070dd
|
||||
background-color: #125BA2
|
||||
color: white
|
||||
.label-master
|
||||
background-color: #a335ee
|
||||
.label-royal
|
||||
background-color: #7313B4
|
||||
color: white
|
||||
|
||||
#market-tab
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
"use strict";
|
||||
|
||||
window.habitrpg = angular.module('habitrpg',
|
||||
['ngRoute', 'ngResource', 'ngSanitize', 'userServices', 'groupServices', 'sharedServices', 'authServices', 'notificationServices', 'ui.bootstrap', 'ui.keypress'])
|
||||
['ngRoute', 'ngResource', 'ngSanitize', 'userServices', 'groupServices', 'memberServices', 'sharedServices', 'authServices', 'notificationServices', 'ui.bootstrap', 'ui.keypress'])
|
||||
|
||||
.constant("API_URL", "")
|
||||
.constant("STORAGE_USER_ID", 'habitrpg-user')
|
||||
|
|
|
|||
|
|
@ -1,40 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
/**
|
||||
* The character controller:
|
||||
*
|
||||
*/
|
||||
|
||||
habitrpg.controller('CharacterCtrl',
|
||||
['$scope', '$location', 'User',
|
||||
function($scope, $location, User) {
|
||||
|
||||
$scope.user = User.user;
|
||||
|
||||
$scope.equipped = function(user, type) {
|
||||
var tier = (user.backer && user.backer.tier)
|
||||
return window.habitrpgShared.helpers.equipped(type, user.items[type], user.preferences, tier);
|
||||
}
|
||||
|
||||
$scope.$watch('user.tasks', function(){
|
||||
$scope.hpPercent = function(hp) {
|
||||
return (hp / 50) * 100;
|
||||
}
|
||||
|
||||
$scope.expPercent = function(exp, level) {
|
||||
return (exp / window.habitrpgShared.algos.tnl(level)) * 100;
|
||||
}
|
||||
})
|
||||
|
||||
$scope.floor = Math.floor;
|
||||
$scope.count = function(arr) {
|
||||
return _.size(arr);
|
||||
}
|
||||
$scope.tnl = window.habitrpgShared.algos.tnl;
|
||||
|
||||
$scope.showUserAvatar = function() {
|
||||
$('.userAvatar').show()
|
||||
}
|
||||
|
||||
}
|
||||
]);
|
||||
|
|
@ -1,17 +1,37 @@
|
|||
"use strict";
|
||||
|
||||
habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Groups', '$http', 'API_URL', '$q', 'User',
|
||||
function($scope, $rootScope, Groups, $http, API_URL, $q, User) {
|
||||
habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Groups', '$http', 'API_URL', '$q', 'User', 'Members', '$location',
|
||||
function($scope, $rootScope, Groups, $http, API_URL, $q, User, Members, $location) {
|
||||
|
||||
$scope.isMember = function(user, group){
|
||||
return ~(group.members.indexOf(user._id));
|
||||
}
|
||||
|
||||
$scope.groups = Groups.groups;
|
||||
// ------ Loading ------
|
||||
|
||||
$scope.groups = Groups.groups;
|
||||
$scope.fetchGuilds = Groups.fetchGuilds;
|
||||
$scope.fetchTavern = Groups.fetchTavern;
|
||||
|
||||
// ------ Modals ------
|
||||
|
||||
$scope.clickMember = function(uid, forceShow) {
|
||||
if (User.user._id == uid && !forceShow) {
|
||||
if ($location.path() == '/tasks') {
|
||||
$location.path('/options');
|
||||
} else {
|
||||
$location.path('/tasks');
|
||||
}
|
||||
} else {
|
||||
// We need the member information up top here, but then we pass it down to the modal controller
|
||||
// down below. Better way of handling this?
|
||||
Members.selectMember(uid);
|
||||
$rootScope.modals.member = true;
|
||||
}
|
||||
}
|
||||
|
||||
// ------ Invites ------
|
||||
|
||||
$scope.invitee = '';
|
||||
$scope.invite = function(group, uuid){
|
||||
group.$invite({uuid:uuid}, function(){
|
||||
|
|
@ -22,6 +42,15 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Groups', '$http', 'A
|
|||
}
|
||||
])
|
||||
|
||||
.controller("MemberModalCtrl", ['$scope', '$rootScope', 'Members',
|
||||
function($scope, $rootScope, Members) {
|
||||
// We watch Members.selectedMember because it's asynchronously set, so would be a hassle to handle updates here
|
||||
$scope.$watch( function() { return Members.selectedMember; }, function (member) {
|
||||
$scope.profile = member;
|
||||
});
|
||||
}
|
||||
])
|
||||
|
||||
.controller('ChatCtrl', ['$scope', 'Groups', 'User', function($scope, Groups, User){
|
||||
$scope._chatMessage = '';
|
||||
$scope.postChat = function(group, message){
|
||||
|
|
@ -38,10 +67,10 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Groups', '$http', 'A
|
|||
}
|
||||
$scope.nameTagClasses = function(message){
|
||||
if (message.contributor) {
|
||||
if (message.contributor.match(/npc/i) || message.contributor.match(/master/i)) {
|
||||
return 'label-master'; // master
|
||||
if (message.contributor.match(/npc/i) || message.contributor.match(/royal/i)) {
|
||||
return 'label-royal';
|
||||
} else if (message.contributor.match(/champion/i)) {
|
||||
return 'label-champion'; // champion
|
||||
return 'label-champion';
|
||||
} else if (message.contributor.match(/elite/i)) {
|
||||
return 'label-success'; //elite
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,12 +18,15 @@ habitrpg.controller("MarketCtrl", ['$rootScope', '$scope', 'User', 'API_URL', '$
|
|||
var message = "Buy this " + (type == 'egg' ? 'egg' : 'hatching potion') + " with " + item.value + " of your " + gems + " Gems?"
|
||||
|
||||
if(confirm(message)){
|
||||
var toPush = type === 'egg' ? item : item.name;
|
||||
$http.post(API_URL + '/api/v1/market/buy?type=' + type, toPush)
|
||||
$http.post(API_URL + '/api/v1/market/buy?type=' + type, item)
|
||||
.success(function(data){
|
||||
User.user.balance = data.balance;
|
||||
store.push(toPush);
|
||||
//$sc¡ope.items = data.items.eggs; // FIXME this isn't updating the UI
|
||||
// don't know what's going on, but trying to work with the returned data (a) isn't updating the ui, (b) isnt'
|
||||
// stickign between refreshes until a force-refresh is called (user._v--).
|
||||
User.user._v--;
|
||||
User.log({});
|
||||
//User.user.balance = data.balance;
|
||||
store.push(type === 'egg' ? item : item.name);
|
||||
//$scope.items = data.items.eggs; // FIXME this isn't updating the UI
|
||||
}).error(function(data){
|
||||
alert(data);
|
||||
console.error(data);
|
||||
|
|
|
|||
|
|
@ -6,17 +6,4 @@ habitrpg.controller("UserAvatarCtrl", ['$scope', '$location', 'User',
|
|||
$scope.hideUserAvatar = function() {
|
||||
$(".userAvatar").hide();
|
||||
};
|
||||
|
||||
$scope.clickAvatar = function(profile) {
|
||||
if (User.user.id == profile.id) {
|
||||
if ($location.path() == '/tasks') {
|
||||
$location.path('/options');
|
||||
} else {
|
||||
$location.path('/tasks');
|
||||
}
|
||||
} else {
|
||||
//TODO show party member modal
|
||||
//$("#avatar-modal-#{uid}").modal('show')
|
||||
}
|
||||
}
|
||||
}]);
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
*/
|
||||
|
||||
angular.module('groupServices', ['ngResource']).
|
||||
factory('Groups', ['API_URL', '$resource', 'User', '$q',
|
||||
function(API_URL, $resource, User, $q) {
|
||||
factory('Groups', ['API_URL', '$resource', 'User', '$q', 'Members',
|
||||
function(API_URL, $resource, User, $q, Members) {
|
||||
var Group = $resource(API_URL + '/api/v1/groups/:gid',
|
||||
{gid:'@_id'},
|
||||
{
|
||||
|
|
@ -34,6 +34,7 @@ angular.module('groupServices', ['ngResource']).
|
|||
// But we don't defer triggering Party, since we always need it for the header if nothing else
|
||||
Group.query({type:'party'}, function(_groups){
|
||||
partyQ.resolve(_groups[0]);
|
||||
Members.populate(_groups[0]);
|
||||
})
|
||||
|
||||
return {
|
||||
|
|
@ -42,10 +43,12 @@ angular.module('groupServices', ['ngResource']).
|
|||
$('#loading-indicator').show();
|
||||
Group.query({type:'guilds'}, function(_groups){
|
||||
guildsQ.resolve(_groups);
|
||||
Members.populate(_groups);
|
||||
$('#loading-indicator').hide();
|
||||
})
|
||||
Group.query({type:'public'}, function(_groups){
|
||||
publicQ.resolve(_groups);
|
||||
Members.populate(_groups);
|
||||
})
|
||||
}),
|
||||
|
||||
|
|
@ -54,6 +57,7 @@ angular.module('groupServices', ['ngResource']).
|
|||
Group.query({type:'tavern'}, function(_groups){
|
||||
$('#loading-indicator').hide();
|
||||
tavernQ.resolve(_groups[0]);
|
||||
Members.populate(_groups[0]);
|
||||
})
|
||||
}),
|
||||
|
||||
|
|
|
|||
73
public/js/services/memberServices.js
Normal file
73
public/js/services/memberServices.js
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
'use strict';
|
||||
|
||||
/**
|
||||
* Services that persists and retrieves user from localStorage.
|
||||
*/
|
||||
|
||||
angular.module('memberServices', ['ngResource']).
|
||||
factory('Members', ['API_URL', '$resource',
|
||||
function(API_URL, $resource) {
|
||||
var members = {};
|
||||
var Member = $resource(API_URL + '/api/v1/members/:uid', {uid:'@_id'});
|
||||
return {
|
||||
|
||||
members: members,
|
||||
|
||||
/**
|
||||
* Allows us to lazy-load party / group / public members throughout the application.
|
||||
* @param obj - either a group or an individual member. If it's a group, we lazy-load all of its members.
|
||||
*/
|
||||
populate: function(obj){
|
||||
|
||||
function populateGroup(group){
|
||||
_.each(group.members, function(member){
|
||||
// meaning `populate('members')` wasn't run on the server, so we're getting the "in-database" form of
|
||||
// the members array, which is just a list of IDs - not the populated objects
|
||||
if (_.isString(member)) return;
|
||||
|
||||
// lazy-load
|
||||
if (!members[member._id]) members[member._id] = member;
|
||||
})
|
||||
}
|
||||
|
||||
// Array of groups
|
||||
if (_.isArray(obj)) {
|
||||
if (obj[0] && obj[0].members) {
|
||||
_.each(obj, function(group){
|
||||
populateGroup(group);
|
||||
})
|
||||
}
|
||||
|
||||
// Individual Group
|
||||
} else if (obj.members)
|
||||
populateGroup(obj);
|
||||
|
||||
// individual Member
|
||||
if (obj._id) {
|
||||
if (!members[obj._id]) {
|
||||
members[obj._id] = obj;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
selectedMember: undefined,
|
||||
|
||||
/**
|
||||
* Once users are populated, we fetch them throughout the application (eg, modals). This
|
||||
* either gets them or fetches if not available
|
||||
* @param uid
|
||||
*/
|
||||
selectMember: function(uid) {
|
||||
var self = this;
|
||||
if (members[uid]) {
|
||||
self.selectedMember = members[uid];
|
||||
} else {
|
||||
Member.get({uid: uid}, function(member){
|
||||
self.populate(member); // lazy load for later
|
||||
self.selectedMember = members[member._id];
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
|
@ -19,6 +19,14 @@ var api = module.exports;
|
|||
var usernameFields = 'auth.local.username auth.facebook.displayName auth.facebook.givenName auth.facebook.familyName auth.facebook.name';
|
||||
var partyFields = 'profile preferences items stats achievements party backer flags.rest ' + usernameFields;
|
||||
|
||||
api.getMember = function(req, res) {
|
||||
User.findById(req.params.uid).select(partyFields).exec(function(err, user){
|
||||
if (err) return res.json(500,{err:err});
|
||||
if (!user) return res.json(400,{err:'User not found'});
|
||||
res.json(user);
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Get groups. If req.query.type privided, returned as an array (so ngResource can use). If not, returned as
|
||||
* object {guilds, public, party, tavern}. req.query.type can be comma-separated `type=guilds,party`
|
||||
|
|
|
|||
|
|
@ -1,39 +0,0 @@
|
|||
var nconf = require('nconf');
|
||||
|
||||
process.on("uncaughtException", function(error) {
|
||||
var sendEmail;
|
||||
sendEmail = function(mailData) {
|
||||
var nodemailer, smtpTransport;
|
||||
nodemailer = require("derby-auth/node_modules/nodemailer");
|
||||
/* create reusable transport method (opens pool of SMTP connections)*/
|
||||
|
||||
/* TODO derby-auth isn't currently configurable here, if you need customizations please send pull request*/
|
||||
|
||||
smtpTransport = nodemailer.createTransport("SMTP", {
|
||||
service: nconf.get('SMTP_SERVICE'),
|
||||
auth: {
|
||||
user: nconf.get('SMTP_USER'),
|
||||
pass: nconf.get('SMTP_PASS')
|
||||
}
|
||||
});
|
||||
/* send mail with defined transport object*/
|
||||
|
||||
return smtpTransport.sendMail(mailData, function(error, response) {
|
||||
if (error) {
|
||||
console.log(error);
|
||||
} else {
|
||||
console.log("Message sent: " + response.message);
|
||||
}
|
||||
/* shut down the connection pool, no more messages*/
|
||||
|
||||
return smtpTransport.close();
|
||||
});
|
||||
};
|
||||
sendEmail({
|
||||
from: "HabitRPG <admin@habitrpg.com>",
|
||||
to: "tylerrenelle@gmail.com",
|
||||
subject: "HabitRPG Error",
|
||||
text: error.stack
|
||||
});
|
||||
return console.log(error.stack);
|
||||
});
|
||||
|
|
@ -143,8 +143,8 @@ var UserSchema = new Schema({
|
|||
/*FIXME store as Date?*/
|
||||
|
||||
lastCron: {
|
||||
type: Number,
|
||||
'default': +(new Date)
|
||||
type: Date,
|
||||
'default': Date.now
|
||||
},
|
||||
/* FIXME remove?*/
|
||||
|
||||
|
|
|
|||
|
|
@ -64,6 +64,9 @@ router.post('/groups/:gid/chat', auth.auth, groups.attachGroup, groups.postChat)
|
|||
//PUT /groups/:gid/chat/:messageId
|
||||
//DELETE /groups/:gid/chat/:messageId
|
||||
|
||||
/* Members */
|
||||
router.get('/members/:uid', groups.getMember);
|
||||
|
||||
// Market
|
||||
router.post('/market/buy', auth.auth, user.marketBuy);
|
||||
|
||||
|
|
|
|||
|
|
@ -5,13 +5,23 @@ var http = require("http");
|
|||
var path = require("path");
|
||||
var app = express();
|
||||
var nconf = require('nconf');
|
||||
var utils = require('./utils');
|
||||
var middleware = require('./middleware');
|
||||
var server;
|
||||
var TWO_WEEKS = 1000 * 60 * 60 * 24 * 14;
|
||||
|
||||
// ------------ Setup configurations ------------
|
||||
require('./config');
|
||||
require('./errors');
|
||||
process.on("uncaughtException", function(error) {
|
||||
// when we hit an error, send it to admin as an email. If no ADMIN_EMAIL is present, just send it to yourself (SMTP_USER)
|
||||
utils.sendEmail({
|
||||
from: "HabitRPG <" + nconf.get('SMTP_USER') + ">",
|
||||
to: nconf.get('ADMIN_EMAIL') || nconf.get('SMTP_USER'),
|
||||
subject: "HabitRPG Error",
|
||||
text: error.stack
|
||||
});
|
||||
console.error(error.stack);
|
||||
});
|
||||
|
||||
// ------------ MongoDB Configuration ------------
|
||||
mongoose = require('mongoose');
|
||||
|
|
|
|||
|
|
@ -52,13 +52,13 @@ html
|
|||
script(type='text/javascript', src='/js/services/sharedServices.js')
|
||||
script(type='text/javascript', src='/js/services/userServices.js')
|
||||
script(type='text/javascript', src='/js/services/groupServices.js')
|
||||
script(type='text/javascript', src='/js/services/memberServices.js')
|
||||
|
||||
script(type='text/javascript', src='/js/filters/filters.js')
|
||||
|
||||
script(type='text/javascript', src='/js/directives/directives.js')
|
||||
|
||||
script(type='text/javascript', src='/js/controllers/authCtrl.js')
|
||||
script(type='text/javascript', src='/js/controllers/characterCtrl.js')
|
||||
script(type='text/javascript', src='/js/controllers/menuCtrl.js')
|
||||
script(type='text/javascript', src='/js/controllers/notificationCtrl.js')
|
||||
script(type='text/javascript', src='/js/controllers/rootCtrl.js')
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
li(ng-repeat='message in group.chat', ng-class='{highlight: message.text.indexOf(username(user.auth,user.profile.name)) != -1}')
|
||||
span.label.chat-message(class='{{nameTagClasses(message)}}', tooltip='{{message.contributor}}')
|
||||
a.label.chat-message(class='{{nameTagClasses(message)}}', tooltip='{{message.contributor}}', ng-click='clickMember(message.uuid, true)')
|
||||
| {{message.user}}
|
||||
span
|
||||
span(ng-bind-html="message.text | linky:'_blank'") -
|
||||
|
|
|
|||
|
|
@ -1,68 +1,66 @@
|
|||
div#groups-controller(ng-controller='GroupsCtrl')
|
||||
// FIXME note, due to https://github.com/angular-ui/bootstrap/issues/783 we can't use nested angular-bootstrap tabs
|
||||
// Subscribe to that ticket & change this when they fix
|
||||
|
||||
// FIXME note, due to https://github.com/angular-ui/bootstrap/issues/783 we can't use nested angular-bootstrap tabs
|
||||
// Subscribe to that ticket & change this when they fix
|
||||
ul.nav.nav-tabs
|
||||
li.active
|
||||
a(data-target='#groups-party', data-toggle='tab') Party
|
||||
li
|
||||
a(data-target='#groups-guilds', data-toggle='tab', ng-click='fetchGuilds()') Guilds
|
||||
|
||||
ul.nav.nav-tabs
|
||||
li.active
|
||||
a(data-target='#groups-party', data-toggle='tab') Party
|
||||
li
|
||||
a(data-target='#groups-guilds', data-toggle='tab', ng-click='fetchGuilds()') Guilds
|
||||
.tab-content(ng-controller='PartyCtrl')
|
||||
#groups-party.tab-pane.active
|
||||
div(ng-show='group._id')
|
||||
include ./group
|
||||
div(ng-hide='group._id')
|
||||
div(ng-show='user.invitations.party')
|
||||
// #with required for the accept/reject buttons
|
||||
// {#with _user.invitations.party as :party}
|
||||
h2 You're Invited To {{user.invitations.party.name}}
|
||||
a.btn.btn-success(data-type='party', ng-click='join(user.invitations.party)') Accept
|
||||
a.btn.btn-danger(ng-click='reject(group)') Reject
|
||||
// {/}
|
||||
div(ng-hide='user.invitations.party', ng-controller='PartyCtrl')
|
||||
h2 Create A Party
|
||||
p
|
||||
| You are not in a party. You can either create one and invite friends, or if you want to join an existing party, have them enter:
|
||||
pre.prettyprint.
|
||||
{{user.id}}
|
||||
include ./create-group
|
||||
|
||||
.tab-content(ng-controller='PartyCtrl')
|
||||
#groups-party.tab-pane.active
|
||||
div(ng-show='group._id')
|
||||
#groups-guilds.tab-pane(ng-controller='GuildsCtrl')
|
||||
ul.nav.nav-tabs
|
||||
li.active
|
||||
a(data-target='#groups-public-guilds', data-toggle='tab') Public Guilds
|
||||
li(ng-repeat='group in groups.guilds')
|
||||
a(data-target='#groups-guild-{{group._id}}', data-toggle='tab') {{group.name}}
|
||||
li
|
||||
a(data-target='#groups-create-guild', data-toggle='tab') Create Guild
|
||||
.tab-content
|
||||
.tab-pane.active#groups-public-guilds
|
||||
div(ng-repeat='invitation in user.invitations.guilds')
|
||||
h3 You're Invited To {{invitation.name}}
|
||||
a.btn.btn-success(data-type='guild', ng-click='join(group)') Accept
|
||||
a.btn.btn-danger(x-bind='click:rejectInvitation') Reject
|
||||
// Public Groups
|
||||
.options-group.option-large.whatever-options
|
||||
input.option-content(type='text',ng-model='guildSearch', placeholder='Search')
|
||||
table.table.table-striped
|
||||
tr(ng-repeat='group in groups.public | filter:guildSearch')
|
||||
td
|
||||
ul.pull-right.challenge-accordion-header-specs
|
||||
li {{group.members.length}} member(s)
|
||||
li
|
||||
// join / leave
|
||||
a.btn.btn-small.btn-danger(ng-show='isMember(user, group)', ng-click='leave(group)')
|
||||
i.icon-ban-circle
|
||||
| Leave
|
||||
a.btn.btn-small.btn-success(ng-hide='isMember(user, group)', ng-click='join(group)')
|
||||
i.icon-ok
|
||||
| Join
|
||||
h4 {{group.name}}
|
||||
p {{group.description}}
|
||||
.tab-pane(id='groups-guild-{{group._id}}', ng-repeat='group in groups.guilds')
|
||||
include ./group
|
||||
div(ng-hide='group._id')
|
||||
div(ng-show='user.invitations.party')
|
||||
// #with required for the accept/reject buttons
|
||||
// {#with _user.invitations.party as :party}
|
||||
h2 You're Invited To {{user.invitations.party.name}}
|
||||
a.btn.btn-success(data-type='party', ng-click='join(user.invitations.party)') Accept
|
||||
a.btn.btn-danger(ng-click='reject(group)') Reject
|
||||
// {/}
|
||||
div(ng-hide='user.invitations.party', ng-controller='PartyCtrl')
|
||||
h2 Create A Party
|
||||
p
|
||||
| You are not in a party. You can either create one and invite friends, or if you want to join an existing party, have them enter:
|
||||
pre.prettyprint.
|
||||
{{user.id}}
|
||||
include ./create-group
|
||||
|
||||
#groups-guilds.tab-pane(ng-controller='GuildsCtrl')
|
||||
ul.nav.nav-tabs
|
||||
li.active
|
||||
a(data-target='#groups-public-guilds', data-toggle='tab') Public Guilds
|
||||
li(ng-repeat='group in groups.guilds')
|
||||
a(data-target='#groups-guild-{{group._id}}', data-toggle='tab') {{group.name}}
|
||||
li
|
||||
a(data-target='#groups-create-guild', data-toggle='tab') Create Guild
|
||||
.tab-content
|
||||
.tab-pane.active#groups-public-guilds
|
||||
div(ng-repeat='invitation in user.invitations.guilds')
|
||||
h3 You're Invited To {{invitation.name}}
|
||||
a.btn.btn-success(data-type='guild', ng-click='join(group)') Accept
|
||||
a.btn.btn-danger(x-bind='click:rejectInvitation') Reject
|
||||
// Public Groups
|
||||
.options-group.option-large.whatever-options
|
||||
input.option-content(type='text',ng-model='guildSearch', placeholder='Search')
|
||||
table.table.table-striped
|
||||
tr(ng-repeat='group in groups.public | filter:guildSearch')
|
||||
td
|
||||
ul.pull-right.challenge-accordion-header-specs
|
||||
li {{group.members.length}} member(s)
|
||||
li
|
||||
// join / leave
|
||||
a.btn.btn-small.btn-danger(ng-show='isMember(user, group)', ng-click='leave(group)')
|
||||
i.icon-ban-circle
|
||||
| Leave
|
||||
a.btn.btn-small.btn-success(ng-hide='isMember(user, group)', ng-click='join(group)')
|
||||
i.icon-ok
|
||||
| Join
|
||||
h4 {{group.name}}
|
||||
p {{group.description}}
|
||||
.tab-pane(id='groups-guild-{{group._id}}', ng-repeat='group in groups.guilds')
|
||||
include ./group
|
||||
|
||||
.tab-pane#groups-create-guild
|
||||
include ./create-group
|
||||
.tab-pane#groups-create-guild
|
||||
include ./create-group
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
<market:>
|
||||
|
||||
<!-- pets pane -->
|
||||
<div id="market-tab">
|
||||
<table><tr>
|
||||
<td><div class="NPC-Alex pull-left"></div></td>
|
||||
<td>
|
||||
<div class="popover static-popover fade right in">
|
||||
<div class="arrow"></div>
|
||||
<h3 class="popover-title">Alexander Augustin</h3>
|
||||
<div class="popover-content">Welcome to the Market! I'm the merchant, <a target="_blank" href="http://www.kickstarter.com/profile/523661924">Alexander</a>. Dying to get that particular pet you're after, but don't want to wait for it to drop? Buy it here!</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr></table>
|
||||
|
||||
<h4>Eggs</h4>
|
||||
{#with _items.pets as :egg}
|
||||
<table class="tab-pane pet-grid">
|
||||
<tr>
|
||||
{#with :egg[0]}<app:pets:egg />{/}
|
||||
{#with :egg[1]}<app:pets:egg />{/}
|
||||
{#with :egg[2]}<app:pets:egg />{/}
|
||||
{#with :egg[3]}<app:pets:egg />{/}
|
||||
</tr>
|
||||
<tr>
|
||||
{#with :egg[4]}<app:pets:egg />{/}
|
||||
{#with :egg[5]}<app:pets:egg />{/}
|
||||
{#with :egg[6]}<app:pets:egg />{/}
|
||||
{#with :egg[7]}<app:pets:egg />{/}
|
||||
</tr>
|
||||
<tr>
|
||||
{#with :egg[8]}<app:pets:egg />{/}
|
||||
</tr>
|
||||
</table>
|
||||
{/}
|
||||
<h4>Hatching Potions</h4>
|
||||
{#with _items.hatchingPotions as :hatchingPotion}
|
||||
<table class="tab-pane pet-grid">
|
||||
<tr>
|
||||
{#with :hatchingPotion[0]}<app:pets:hatchingPotion />{/}
|
||||
{#with :hatchingPotion[1]}<app:pets:hatchingPotion />{/}
|
||||
{#with :hatchingPotion[2]}<app:pets:hatchingPotion />{/}
|
||||
{#with :hatchingPotion[3]}<app:pets:hatchingPotion />{/}
|
||||
</tr>
|
||||
<tr>
|
||||
{#with :hatchingPotion[4]}<app:pets:hatchingPotion />{/}
|
||||
{#with :hatchingPotion[5]}<app:pets:hatchingPotion />{/}
|
||||
{#with :hatchingPotion[6]}<app:pets:hatchingPotion />{/}
|
||||
{#with :hatchingPotion[7]}<app:pets:hatchingPotion />{/}
|
||||
</tr>
|
||||
<tr>
|
||||
{#with :hatchingPotion[8]}<app:pets:hatchingPotion />{/}
|
||||
{#with :hatchingPotion[9]}<app:pets:hatchingPotion />{/}
|
||||
</tr>
|
||||
</table>
|
||||
{/}
|
||||
</div>
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
figure.herobox(ng-click='clickAvatar(profile)', data-name='{{username(profile.auth, profile.profile.name)}}', ng-class='{isUser: profile.id==user.id, hasPet: profile.items.pet}', data-level='{{profile.stats.lvl}}', data-uid='{{profile.id}}', rel='popover', data-placement='bottom', data-trigger='hover', data-html='true', data-content="<div ng-hide='profile.id == user.id'> <div class='progress progress-danger' style='height:5px;'> <div class='bar' style='height: 5px; width: {{percent(profile.stats.hp, 50)}}%;'></div> </div> <div class='progress progress-warning' style='height:5px;'> <div class='bar' style='height: 5px; width: {{percent(profile.stats.exp, tnl(profile.stats.lvl))}}%;'></div> </div> <div>Level: {{profile.stats.lvl}}</div> <div>GP: {{profile.stats.gp | number:0}}</div> <div>{{count(profile.items.pets)}} / 90 Pets Found</div> </div>")
|
||||
figure.herobox(ng-click='clickMember(profile._id)', data-name='{{username(profile.auth, profile.profile.name)}}', ng-class='{isUser: profile.id==user.id, hasPet: profile.items.pet}', data-level='{{profile.stats.lvl}}', data-uid='{{profile.id}}', rel='popover', data-placement='bottom', data-trigger='hover', data-html='true', data-content="<div ng-hide='profile.id == user.id'> <div class='progress progress-danger' style='height:5px;'> <div class='bar' style='height: 5px; width: {{percent(profile.stats.hp, 50)}}%;'></div> </div> <div class='progress progress-warning' style='height:5px;'> <div class='bar' style='height: 5px; width: {{percent(profile.stats.exp, tnl(profile.stats.lvl))}}%;'></div> </div> <div>Level: {{profile.stats.lvl}}</div> <div>GP: {{profile.stats.gp | number:0}}</div> <div>{{count(profile.items.pets)}} / 90 Pets Found</div> </div>")
|
||||
.character-sprites
|
||||
span(ng-class='{zzz:profile.flags.rest}')
|
||||
span(class='{{profile.preferences.gender}}_skin_{{profile.preferences.skin}}')
|
||||
|
|
|
|||
|
|
@ -4,4 +4,5 @@ include ./reroll
|
|||
include ./death
|
||||
include ./new-stuff
|
||||
include ./why-ads
|
||||
include ./more-gems
|
||||
include ./more-gems
|
||||
include ./members
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
{{#each _membersArray as :member}}
|
||||
<app:modals:modal modalId="avatar-modal-{{:member.id}}">
|
||||
<app:avatar:profile profile={{_members[:member.id]}} />
|
||||
|
||||
<!-- see above -->
|
||||
<profile:>
|
||||
<h2 class='profile-modal-header'>{{username(@profile.auth, @profile.profile.name)}}</h2>
|
||||
<hr/>
|
||||
<div class='row-fluid'>
|
||||
<div class='span6'>
|
||||
{{#if @profile.profile.imageUrl}}
|
||||
<img src="{{@profile.profile.imageUrl}}" />
|
||||
{{/}}
|
||||
{{#if @profile.profile.blurb}}
|
||||
<p>{{@profile.profile.blurb}}</p>
|
||||
{{/}}
|
||||
{{#if @profile.profile.websites}}
|
||||
<ul>
|
||||
{{#each @profile.profile.websites as :website}}
|
||||
<li>{{:website}}</li>
|
||||
{{/}}
|
||||
</ul>
|
||||
{{/}}
|
||||
<h3>Stats</h3>
|
||||
<app:avatar:profile-stats profile={{@profile}} />
|
||||
</div>
|
||||
<div class='span6'>
|
||||
<h3>Achievements</h3>
|
||||
<app:avatar:achievements profile={{@profile}} />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<@footer>
|
||||
<button data-dismiss="modal" class="btn btn-success">Ok</button>
|
||||
</@footer>
|
||||
</app:modals:modal>
|
||||
{{/}}
|
||||
22
views/shared/modals/members.jade
Normal file
22
views/shared/modals/members.jade
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
div(ng-controller='MemberModalCtrl')
|
||||
div(modal='modals.member')
|
||||
.modal-header
|
||||
h3
|
||||
span {{username(profile.auth, profile.profile.name)}}
|
||||
span(ng-show='profile.backer.contributor') - {{profile.backer.contributor}}
|
||||
.modal-body
|
||||
app:avatar:profile
|
||||
.row-fluid
|
||||
.span6
|
||||
img(ng-show='profile.profile.imageUrl', ng-src='{{profile.profile.imageUrl}}')
|
||||
p(ng-show='profile.profile.blurb') {{profile.profile.blurb}}
|
||||
ul(ng-show='profile.profile.websites')
|
||||
li(ng-repeat='website in profile.profile.websites') {{website}}
|
||||
h3 Stats
|
||||
include ../profiles/stats
|
||||
.span6
|
||||
include ../header/avatar
|
||||
h3 Achievements
|
||||
include ../profiles/achievements
|
||||
.modal-footer
|
||||
button.btn.btn-default(ng-click='modals.member = false') Ok
|
||||
|
|
@ -13,58 +13,65 @@
|
|||
// <div class='achievement achievement-shield'></div>
|
||||
// <div class='achievement achievement-karaoke'></div>
|
||||
|
||||
.achievement.achievement-helm(ng-if='profile.backer.npc')
|
||||
div(ng-if='profile.backer.npc')
|
||||
.achievement.achievement-helm
|
||||
h5
|
||||
span.label.label-success {{profile.backer.npc}} NPC
|
||||
span.label.label-master {{profile.backer.npc}} NPC
|
||||
small Backed the Kickstarter project at the maximum level!
|
||||
hr
|
||||
|
||||
.achievement.achievement-firefox(ng-if='profile.backer.contributor')
|
||||
div(ng-class='{muted: !profile.backer.contributor, hidden: user.id!=profile.id}')
|
||||
h5
|
||||
span.label.label-inverse(ng-if='profile.backer.contributor') {{profile.backer.contributor}}
|
||||
span.label(ng-if='!profile.backer.contributor') Contributor
|
||||
small
|
||||
| Has contributed to HabitRPG (code, design, pixel art, legal advice, docs, etc). Want this badge? Fix a bug :)
|
||||
div(ng-if='profile.backer.contributor || user._id == profile._id')
|
||||
.achievement.achievement-firefox(ng-show='profile.backer.contributor')
|
||||
div(ng-class='{muted: !profile.backer.contributor}')
|
||||
h5
|
||||
span.label.label-inverse(ng-if='profile.backer.contributor') {{profile.backer.contributor}}
|
||||
span.label(ng-if='!profile.backer.contributor') Contributor
|
||||
small.
|
||||
Has contributed to HabitRPG (code, design, pixel art, legal advice, docs, etc). Want this badge? Fix a bug :)
|
||||
hr
|
||||
|
||||
.achievement.achievement-heart(ng-if='profile.backer.tier')
|
||||
div(ng-if='profile.backer.tier')
|
||||
.achievement.achievement-heart
|
||||
h5 Kickstarter Backer - ${{profile.backer.tier}} Tier
|
||||
small Backed the Kickstarter Project
|
||||
hr
|
||||
|
||||
.achievement.achievement-thermometer(ng-show='profile.achievements.streak')
|
||||
div(ng-class='{muted: !profile.achievements.streak, hidden: user.id!=profile.id}')
|
||||
h5 {{profile.achievements.streak || 0 }} Streak Achievement(s)
|
||||
small
|
||||
| Has performed {{profile.achievements.streak || 0 }} 21-day streaks on Dailies
|
||||
div(ng-if='profile.achievements.streak || user._id == profile._id')
|
||||
.achievement.achievement-thermometer(ng-show='profile.achievements.streak')
|
||||
div(ng-class='{muted: !profile.achievements.streak}')
|
||||
h5 {{profile.achievements.streak || 0 }} Streak Achievement(s)
|
||||
small Has performed {{profile.achievements.streak || 0 }} 21-day streaks on Dailies
|
||||
hr
|
||||
|
||||
.achievement.achievement-cake(ng-if='profile.achievements.originalUser')
|
||||
div(ng-if='profile.achievements.originalUser')
|
||||
h5 Original User
|
||||
small
|
||||
| Goes way back to the Habit's days of yore (bless them for soldiering through bugs!)
|
||||
div(ng-if='profile.achievements.ultimateGear || user._id == profile._id')
|
||||
.achievement.achievement-armor(ng-show='profile.achievements.ultimateGear')
|
||||
div(ng-class='{muted: !profile.achievements.ultimateGear}')
|
||||
h5 Ultimate Gear
|
||||
small Has upgraded to the maximum weapon and armor set
|
||||
hr
|
||||
|
||||
.achievement.achievement-armor(ng-show='profile.achievements.ultimateGear')
|
||||
div(ng-class='{muted: !profile.achievements.ultimateGear, hidden: user.id!=profile.id}')
|
||||
h5 Ultimate Gear
|
||||
small Has upgraded to the maximum weapon and armor set
|
||||
div(ng-if='profile.achievements.beastMaster || user._id == profile._id')
|
||||
.achievement.achievement-rat(ng-show='profile.achievements.beastMaster')
|
||||
div(ng-class='{muted: !profile.achievements.beastMaster}')
|
||||
h5 Beast Master
|
||||
small Has found all 90 pets (insanely difficult, give this user props!)
|
||||
hr
|
||||
|
||||
.achievement.achievement-rat(ng-show='profile.achievements.beastMaster')
|
||||
div(ng-class='{muted: !profile.achievements.beastMaster, hidden: user.id!=profile.id}')
|
||||
h5 Beast Master
|
||||
small Has found all 90 pets (insanely difficult, give this user props!)
|
||||
hr
|
||||
|
||||
.achievement.achievement-tree(ng-if='profile.achievements.helpedHabit')
|
||||
div(ng-if='profile.achievements.helpedHabit')
|
||||
.achievement.achievement-tree
|
||||
h5 Helped Habit Grow
|
||||
small
|
||||
| Has helped HabitRPG grow by filling out
|
||||
a(href='http://community.habitrpg.com/node/290', target='_blank') this survey.
|
||||
small.
|
||||
Helped HabitRPG grow by filling out <a href='http://community.habitrpg.com/node/290' target='_blank'>this survey.</a>
|
||||
hr
|
||||
|
||||
div(ng-if='profile.achievements.originalUser || profile.achievements.veteranUser')
|
||||
.achievement.achievement-cake
|
||||
div(ng-if='profile.achievements.veteran')
|
||||
h5 Veteran
|
||||
small
|
||||
| Has weathered Habit The Grey (our pre Angular website), and has gained many battle-scars from its bugs.
|
||||
div(ng-if='profile.achievements.originalUser')
|
||||
h5 Original User!
|
||||
small
|
||||
| One of the <em>very</em> original early adopters, Talk about alpha tester!
|
||||
hr
|
||||
Loading…
Reference in a new issue