mirror of
https://github.com/sudoxnym/habitica.git
synced 2026-07-13 15:38:59 +00:00
groups: big changes here i may decide to revert. GET /api/v1/groups
optionally takes ?type=guilds (guilds, party, tavern, public). this allows us to incrementally load the app as they click through the tabs, because guilds is really large (1.5mb!) - gotta cut this down somehow. The big issue is requireing return values as an array, so ngResource can work with the response (subsequent group.$postChat(), group.$join(), etc.). The array format doesn't really work out that well, so I may scrap
This commit is contained in:
parent
524457b5d7
commit
bc4798b354
9 changed files with 130 additions and 46 deletions
|
|
@ -2,26 +2,61 @@
|
|||
|
||||
habitrpg
|
||||
|
||||
.controller("GroupsCtrl", ['$scope', '$rootScope', 'Groups', '$http', '$location', '$http', 'API_URL',
|
||||
function($scope, $rootScope, Groups, $http, API_URL) {
|
||||
$scope._chatMessage = '';
|
||||
$scope.groups = Groups.query(function(groups){
|
||||
$scope.members = groups.members;
|
||||
.controller("GroupsCtrl", ['$scope', '$rootScope', 'Groups', '$http', 'API_URL', '$q',
|
||||
function($scope, $rootScope, Groups, $http, API_URL, $q) {
|
||||
|
||||
// The user may not visit the public guilds, personal guilds, and tavern pages. So
|
||||
// we defer loading them to the html until they've clicked the tabs
|
||||
var partyQ = $q.defer(),
|
||||
guildsQ = $q.defer(),
|
||||
publicQ = $q.defer(),
|
||||
tavernQ = $q.defer();
|
||||
|
||||
$scope.groups = {
|
||||
party: partyQ.promise,
|
||||
guilds: guildsQ.promise,
|
||||
public: publicQ.promise,
|
||||
tavern: tavernQ.promise
|
||||
};
|
||||
|
||||
// But we don't defer triggering Party, since we always need it for the header if nothing else
|
||||
Groups.query({type:'party'}, function(_groups){
|
||||
partyQ.resolve(_groups[0]);
|
||||
})
|
||||
|
||||
// Note the _.once() to make sure it can never be called again
|
||||
$scope.fetchGuilds = _.once(function(){
|
||||
$('#loading-indicator').show();
|
||||
Groups.query({type:'guilds'}, function(_groups){
|
||||
guildsQ.resolve(_groups);
|
||||
$('#loading-indicator').hide();
|
||||
})
|
||||
Groups.query({type:'public'}, function(_groups){
|
||||
publicQ.resolve(_groups);
|
||||
})
|
||||
});
|
||||
|
||||
$scope.fetchTavern = _.once(function(){
|
||||
$('#loading-indicator').show();
|
||||
Groups.query({type:'tavern'}, function(_groups){
|
||||
$('#loading-indicator').hide();
|
||||
tavernQ.resolve(_groups[0]);
|
||||
})
|
||||
});
|
||||
|
||||
//$scope._chatMessage = '';
|
||||
$scope.postChat = function(group, message){
|
||||
//FIXME ng-model makes this painfully slow! using jquery for now, which is really non-angular-like
|
||||
message = $('.chat-textarea').val();
|
||||
if (_.isEmpty(message)) return
|
||||
$('.chat-btn').addClass('disabled');
|
||||
$http.post('/api/v1/groups/'+group._id+'/chat', {message:message})
|
||||
.success(function(data){
|
||||
//$scope._chatMessage = '';
|
||||
$('.chat-textarea').val('');
|
||||
group.chat = data;
|
||||
$('.chat-btn').removeClass('disabled');
|
||||
});
|
||||
group.$postChat({message:message}, function(data){
|
||||
//$scope._chatMessage = '';
|
||||
$('.chat-textarea').val('');
|
||||
group.chat = data.chat;
|
||||
$('.chat-btn').removeClass('disabled');
|
||||
});
|
||||
}
|
||||
$scope.party = true;
|
||||
}
|
||||
])
|
||||
|
||||
|
|
@ -29,6 +64,9 @@ habitrpg
|
|||
function($scope, Groups) {
|
||||
$scope.type = 'guild';
|
||||
$scope.text = 'Guild';
|
||||
$scope.join = function(group){
|
||||
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
|
|
@ -36,17 +74,12 @@ habitrpg
|
|||
function($scope, Groups) {
|
||||
$scope.type = 'party';
|
||||
$scope.text = 'Party';
|
||||
Groups.query(function(groups){
|
||||
$scope.group = groups.party;
|
||||
})
|
||||
$scope.group = $scope.groups.party;
|
||||
}
|
||||
])
|
||||
|
||||
.controller("TavernCtrl", ['$scope', 'Groups',
|
||||
function($scope, Groups) {
|
||||
//FIXME make sure this query is only called once for all these controllers! If not, let's memoize groups at groupServices level
|
||||
Groups.query(function(groups){
|
||||
$scope.group = groups.tavern;
|
||||
});
|
||||
$scope.group = $scope.groups.tavern;
|
||||
}
|
||||
])
|
||||
|
|
@ -10,8 +10,10 @@ angular.module('groupServices', ['ngResource']).
|
|||
var Group = $resource(API_URL + '/api/v1/groups/:gid',
|
||||
{gid:'@_id'},
|
||||
{
|
||||
'query': {method: "GET", isArray:false}
|
||||
//postChat: {method: "POST"}
|
||||
//'query': {method: "GET", isArray:false}
|
||||
postChat: {method: "POST", url: API_URL + '/api/v1/groups/:gid/chat'},
|
||||
join: {method: "POST", url: API_URL + '/api/v1/groups/:gid/join'},
|
||||
leave: {method: "POST", url: API_URL + '/api/v1/groups/:gid/leave'}
|
||||
});
|
||||
|
||||
return Group;
|
||||
|
|
|
|||
BIN
public/page-loader.gif
Normal file
BIN
public/page-loader.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.3 KiB |
|
|
@ -16,13 +16,22 @@ var api = module.exports;
|
|||
------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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`
|
||||
* @param req
|
||||
* @param res
|
||||
* @param next
|
||||
*/
|
||||
api.getGroups = function(req, res, next) {
|
||||
var user = res.locals.user;
|
||||
var usernameFields = 'auth.local.username auth.facebook.displayName auth.facebook.givenName auth.facebook.familyName auth.facebook.name';
|
||||
var type = req.query.type && req.query.type.split(',');
|
||||
|
||||
// First get all groups
|
||||
async.parallel({
|
||||
party: function(cb) {
|
||||
if (type && !~type.indexOf('party')) return cb(null, {});
|
||||
Group
|
||||
.findOne({type: 'party', members: {'$in': [user._id]}})
|
||||
.populate({
|
||||
|
|
@ -33,13 +42,16 @@ api.getGroups = function(req, res, next) {
|
|||
.exec(cb);
|
||||
},
|
||||
guilds: function(cb) {
|
||||
if (type && !~type.indexOf('guilds')) return cb(null, []);
|
||||
Group.find({type: 'guild', members: {'$in': [user._id]}}).populate('members', usernameFields).exec(cb);
|
||||
// Group.find({type: 'guild', members: {'$in': [user._id]}}, cb);
|
||||
},
|
||||
tavern: function(cb) {
|
||||
if (type && !~type.indexOf('tavern')) return cb(null, {});
|
||||
Group.findOne({_id: 'habitrpg'}, cb);
|
||||
},
|
||||
"public": function(cb) {
|
||||
if (type && !~type.indexOf('public')) return cb(null, []);
|
||||
Group.find({privacy: 'public'}, {name:1, description:1, members:1}, cb);
|
||||
}
|
||||
}, function(err, results){
|
||||
|
|
@ -52,7 +64,15 @@ api.getGroups = function(req, res, next) {
|
|||
// Sort public groups by members length (not easily doable in mongoose)
|
||||
results.public = _.sortBy(results.public, function(group){
|
||||
return -group.members.length;
|
||||
})
|
||||
});
|
||||
|
||||
// If they're requesting a specific type, let's return it as an array so that $ngResource
|
||||
// can utilize it properly
|
||||
if (type) {
|
||||
results = _.reduce(type, function(m,t){
|
||||
return m.concat(_.isArray(results[t]) ? results[t] : [results[t]]);
|
||||
}, []);
|
||||
}
|
||||
|
||||
res.json(results);
|
||||
})
|
||||
|
|
@ -74,7 +94,7 @@ api.postChat = function(req, res, next) {
|
|||
uuid: user._id,
|
||||
contributor: user.backer && user.backer.contributor,
|
||||
npc: user.backer && user.backer.npc,
|
||||
text: req.body.message,
|
||||
text: req.query.message, // FIXME this should be body, but ngResource is funky
|
||||
user: helpers.username(user.auth, user.profile.name),
|
||||
timestamp: +(new Date)
|
||||
};
|
||||
|
|
@ -87,8 +107,29 @@ api.postChat = function(req, res, next) {
|
|||
user.save();
|
||||
}
|
||||
|
||||
group.save(function(err, group){
|
||||
group.save(function(err, saved){
|
||||
if (err) return res.json(500, {err:err});
|
||||
res.json(group.chat);
|
||||
res.json(saved);
|
||||
})
|
||||
}
|
||||
|
||||
api.join = function(req, res, next) {
|
||||
var user = res.locals.user,
|
||||
group = res.locals.group;
|
||||
|
||||
group.members.push(user._id);
|
||||
group.save(function(err, saved){
|
||||
if (err) return res.json(500,{err:err});
|
||||
res.json(saved);
|
||||
});
|
||||
}
|
||||
|
||||
api.leave = function(req, res, next) {
|
||||
var user = res.locals.user,
|
||||
group = res.locals.group;
|
||||
|
||||
Group.update({_id:group._id},{$pull:{members:user._id}}, function(err){
|
||||
if (err) return res.json(500,{err:err});
|
||||
res.send(200);
|
||||
})
|
||||
}
|
||||
|
|
@ -61,6 +61,9 @@ router.get('/groups', auth, groups.getGroups);
|
|||
//PUT /groups/:gid (edit group)
|
||||
//DELETE /groups/:gid
|
||||
|
||||
router.post('/groups/:gid/join', auth, groups.attachGroup, groups.join);
|
||||
router.post('/groups/:gid/leave', auth, groups.attachGroup, groups.leave);
|
||||
|
||||
//GET /groups/:gid/chat
|
||||
router.post('/groups/:gid/chat', auth, groups.attachGroup, groups.postChat);
|
||||
//PUT /groups/:gid/chat/:messageId
|
||||
|
|
|
|||
|
|
@ -76,27 +76,32 @@ html
|
|||
meta(name='apple-mobile-web-app-capable', content='yes')
|
||||
|
||||
body(ng-app="habitrpg", ng-controller="RootCtrl", ng-cloak)
|
||||
include ./shared/modals/index
|
||||
include ./shared/header/header
|
||||
div(ng-controller='GroupsCtrl')
|
||||
include ./shared/modals/index
|
||||
include ./shared/header/header
|
||||
|
||||
#notification-area(ng-controller='NotificationCtrl')
|
||||
#wrap
|
||||
#notification-area(ng-controller='NotificationCtrl')
|
||||
#wrap
|
||||
|
||||
// Errors
|
||||
ul.unstyled.alert.alert-error(ng-show='_flash.error')
|
||||
li(ng-repeat='error in _flash.error') {{error}}
|
||||
// Errors
|
||||
ul.unstyled.alert.alert-error(ng-show='_flash.error')
|
||||
li(ng-repeat='error in _flash.error') {{error}}
|
||||
|
||||
//if they hide the header, we still need user-menu visible
|
||||
div(ng-if='user.preferences.hideHeader')
|
||||
include ./shared/header/menu
|
||||
//if they hide the header, we still need user-menu visible
|
||||
div(ng-if='user.preferences.hideHeader')
|
||||
include ./shared/header/menu
|
||||
|
||||
.exp-chart(ng-show='page.charts.exp')
|
||||
.exp-chart(ng-show='page.charts.exp')
|
||||
|
||||
#main(ng-view)
|
||||
#main(ng-view)
|
||||
|
||||
.container
|
||||
h3
|
||||
a(target='_blank',href='https://workflowy.com/shared/9c77c53d-a174-d181-73a4-dc611508a936/') Roadmap
|
||||
include ./shared/footer
|
||||
.container
|
||||
h3
|
||||
a(target='_blank',href='https://workflowy.com/shared/9c77c53d-a174-d181-73a4-dc611508a936/') Roadmap
|
||||
include ./shared/footer
|
||||
|
||||
// Modal backdrop for when loading big stuff
|
||||
#loading-indicator(style='display:none;')
|
||||
.modal-backdrop
|
||||
h1(style='position:absolute;top:50%;left:45%;color:white;z-index:9000') Loading...
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ div(ng-controller='GroupsCtrl')
|
|||
li.active
|
||||
a(data-target='#groups-party', data-toggle='tab') Party
|
||||
li
|
||||
a(data-target='#groups-guilds', data-toggle='tab') Guilds
|
||||
a(data-target='#groups-guilds', data-toggle='tab', ng-click='fetchGuilds()') Guilds
|
||||
|
||||
.tab-content
|
||||
#groups-party.tab-pane.active Party
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
i.icon-user
|
||||
| Profile
|
||||
li
|
||||
a(data-toggle='tab',data-target='#groups-tab')
|
||||
a(data-toggle='tab',data-target='#groups-tab', ng-click='fetchParty()')
|
||||
i.icon-heart
|
||||
| Groups
|
||||
li(ng-show='user.flags.dropsEnabled')
|
||||
|
|
@ -21,7 +21,7 @@
|
|||
i.icon-leaf
|
||||
| Stable
|
||||
li
|
||||
a(data-toggle='tab',data-target='tavern-tab')
|
||||
a(data-toggle='tab',data-target='#tavern-tab',ng-click='fetchTavern()')
|
||||
i.icon-eye-close
|
||||
| Tavern
|
||||
li
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
a(x-bind='click:toggleChart', ng-click='notPorted()', data-id='exp', tooltip='Progress')
|
||||
i.icon-signal
|
||||
// party
|
||||
span(ng-controller='GroupsCtrl')
|
||||
span(ng-controller='PartyCtrl')
|
||||
.herobox-wrap(ng-repeat='profile in groups.party.members')
|
||||
include avatar
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue