rewrite: start implementing groups. Note angular-bootstrap 0.4.0 required for nested tabs

This commit is contained in:
Tyler Renelle
2013-08-29 20:18:39 -04:00
parent b1b9728642
commit 17a9469370
14 changed files with 403 additions and 406 deletions
+33 -9
View File
@@ -1,12 +1,36 @@
"use strict";
/*
The authentication controller (login & facebook)
*/
habitrpg
habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Groups', '$http', '$location',
function($scope, $rootScope, Groups) {
$scope.groups = Groups.query();
$scope.party = true;
}
]);
.controller("GroupsCtrl", ['$scope', '$rootScope', 'Groups', '$http', '$location',
function($scope, $rootScope, Groups) {
$scope.groups = Groups.query();
$scope.party = true;
}
])
.controller("GuildsCtrl", ['$scope', 'Groups',
function($scope, Groups) {
$scope.type = 'guild';
$scope.text = 'Guild';
}
])
.controller("PartyCtrl", ['$scope', 'Groups',
function($scope, Groups) {
$scope.type = 'party';
$scope.text = 'Party';
Groups.query(function(groups){
$scope.group = 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;
});
}
])
+1 -1
View File
@@ -18,7 +18,7 @@
"angular": "1.2.0-rc.1",
"angular-resource": "1.2.0-rc.1",
"angular-ui": "~0.4.0",
"angular-bootstrap": "~0.5.0",
"angular-bootstrap": "0.4.0",
"habitrpg-shared": "git://github.com/HabitRPG/habitrpg-shared.git#rewrite",
"lodash": "~1.3.1",
"moment": "~2.1.0",
+1 -1
View File
@@ -8,7 +8,7 @@ _ = require('lodash');
icalendar = require('icalendar');
api = require('./api');
api = require('./user');
/* ---------- Deprecated Paths ------------*/
+66
View File
@@ -0,0 +1,66 @@
// @see ../routes for routing
var _ = require('lodash');
var nconf = require('nconf');
var async = require('async');
var algos = require('habitrpg-shared/script/algos');
var helpers = require('habitrpg-shared/script/helpers');
var items = require('habitrpg-shared/script/items');
var User = require('./../models/user').model;
var Group = require('./../models/group').model;
var api = module.exports;
/*
------------------------------------------------------------------------
Party
------------------------------------------------------------------------
*/
api.getGroups = function(req, res, next) {
var user = res.locals.user;
/*TODO should we support non-authenticated users? just for viewing public groups?*/
return async.parallel({
party: function(cb) {
async.waterfall([
function(cb2) {
Group.findOne({type: 'party', members: {'$in': [user._id]}}, cb2);
}, function(party, cb2) {
var fields, query;
party = party.toJSON();
query = {_id: {
'$in': party.members,
'$nin': [user._id]
}
};
fields = 'profile preferences items stats achievements party backer auth.local.username auth.facebook.first_name auth.facebook.last_name auth.facebook.name auth.facebook.username'.split(' ');
fields = _.reduce(fields, (function(m, k, v) {m[k] = 1;return m;}), {});
User.find(query, fields, function(err, members) {
party.members = members;
cb2(err, party);
});
}
], function(err, members) {
cb(err, members);
});
},
guilds: function(cb) {
Group.find({type: 'guild', members: {'$in': [user._id]}}, cb);
},
tavern: function(cb) {
Group.findOne({_id: 'habitrpg'}, cb);
},
"public": function(cb) {
Group.find({
privacy: 'public'
}, {
name: 1,
description: 1,
members: 1
}, cb);
}
}, function(err, results) {
if (err) return res.json(500, {err: err});
res.json(results);
});
};
+39 -26
View File
@@ -1,19 +1,21 @@
var express = require('express');
var router = new express.Router();
var api = require('../controllers/api');
var user = require('../controllers/user');
var groups = require('../controllers/groups');
/*
---------- /api/v1 API ------------
Every url added to router is prefaced by /api/v1
See ./routes/coffee for routes
v1 API. Requires x-api-user (user id) and x-api-key (api key) headers, Test with:
v1 user. Requires x-api-user (user id) and x-api-key (api key) headers, Test with:
$ cd node_modules/racer && npm install && cd ../..
$ mocha test/api.mocha.coffee
$ mocha test/user.mocha.coffee
*/
var auth, cron, verifyTaskExists;
auth = api.auth, verifyTaskExists = api.verifyTaskExists, cron = api.cron;
var auth = user.auth
var verifyTaskExists = user.verifyTaskExists
var cron = user.cron;
router.get('/status', function(req, res) {
return res.json({
@@ -22,37 +24,48 @@ router.get('/status', function(req, res) {
});
/* Auth*/
router.post('/register', api.registerUser);
router.post('/register', user.registerUser);
/* Scoring*/
router.post('/user/task/:id/:direction', auth, cron, api.scoreTask);
router.post('/user/tasks/:id/:direction', auth, cron, api.scoreTask);
router.post('/user/task/:id/:direction', auth, cron, user.scoreTask);
router.post('/user/tasks/:id/:direction', auth, cron, user.scoreTask);
/* Tasks*/
router.get('/user/tasks', auth, cron, api.getTasks);
router.get('/user/task/:id', auth, cron, api.getTask);
router.put('/user/task/:id', auth, cron, verifyTaskExists, api.updateTask);
router.post('/user/tasks', auth, cron, api.updateTasks);
router["delete"]('/user/task/:id', auth, cron, verifyTaskExists, api.deleteTask);
router.post('/user/task', auth, cron, api.createTask);
router.put('/user/task/:id/sort', auth, cron, verifyTaskExists, api.sortTask);
router.post('/user/clear-completed', auth, cron, api.clearCompleted);
router.get('/user/tasks', auth, cron, user.getTasks);
router.get('/user/task/:id', auth, cron, user.getTask);
router.put('/user/task/:id', auth, cron, verifyTaskExists, user.updateTask);
router.post('/user/tasks', auth, cron, user.updateTasks);
router["delete"]('/user/task/:id', auth, cron, verifyTaskExists, user.deleteTask);
router.post('/user/task', auth, cron, user.createTask);
router.put('/user/task/:id/sort', auth, cron, verifyTaskExists, user.sortTask);
router.post('/user/clear-completed', auth, cron, user.clearCompleted);
/* Items*/
router.post('/user/buy/:type', auth, cron, api.buy);
router.post('/user/buy/:type', auth, cron, user.buy);
/* User*/
router.get('/user', auth, cron, api.getUser);
router.post('/user/auth/local', api.loginLocal);
router.post('/user/auth/facebook', api.loginFacebook);
router.put('/user', auth, cron, api.updateUser);
router.post('/user/revive', auth, cron, api.revive);
router.post('/user/batch-update', auth, cron, api.batchUpdate);
router.post('/user/reroll', auth, cron, api.reroll);
router.post('/user/buy-gems', auth, api.buyGems);
router.get('/user', auth, cron, user.getUser);
router.post('/user/auth/local', user.loginLocal);
router.post('/user/auth/facebook', user.loginFacebook);
router.put('/user', auth, cron, user.updateUser);
router.post('/user/revive', auth, cron, user.revive);
router.post('/user/batch-update', auth, cron, user.batchUpdate);
router.post('/user/reroll', auth, cron, user.reroll);
router.post('/user/buy-gems', auth, user.buyGems);
/* Groups*/
router.get('/groups', auth, api.getGroups);
router.get('/groups', auth, groups.getGroups);
//TODO:
//GET /groups/:gid (get group)
//POST /groups/:gid (create group)
//PUT /groups/:gid (edit group)
//DELETE /groups/:gid
//GET /groups/:gid/chat
//POST /groups/:gid/chat
//PUT /groups/:gid/chat/:messageId
//DELETE /groups/:gid/chat/:messageId
module.exports = router;
-363
View File
@@ -1,363 +0,0 @@
<groups-pane:>
<ul class="nav nav-tabs">
<li class="active"><a data-toggle='tab' data-target="#groups-party">Party</a></li>
<li><a data-toggle='tab' data-target="#groups-guilds">Guilds</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="groups-party">
{#if _party.id}
<app:groups:group group={groups[_party.id]} />
{else if _user.invitations.party}
<!-- #with required for the accept/reject buttons -->
{#with _user.invitations.party as :party}
<h2>You're Invited To {:party.name}</h2>
<a class='btn btn-success' data-type='party' x-bind="click:acceptInvitation">Accept</a>
<a class='btn btn-danger' x-bind="click:rejectInvitation">Reject</a>
{/}
{else}
<h2>Create A Party</h2>
<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:</p>
<pre class=prettyprint>{_user.id}</pre>
<app:groups:create-group type='party' />
{/}
</div>
<div class='tab-pane' id="groups-guilds">
<ul class="nav nav-pills">
<li class=active><a data-toggle='tab' data-target="#groups-guilds-public">Public Guilds</a></li>
{{#each _guilds as :guild}}
<li><a data-toggle='tab' data-target="#groups-guild-{{:guild.id}}">{:guild.name}</a></li>
{{/}}
<li><a data-toggle='tab' data-target="#groups-guild-create">Create Guild</a></li>
</ul>
<div class="tab-content">
<div class='tab-pane active' id='groups-guilds-public'>
<!-- strange bug here - derby paths supposed to work like _user?.invitations?.guilds, wtf? -->
{#if and(_user.invitations,_user.invitations.guilds)}
{#each _user.invitations.guilds as :invitation}
<div>
<h3>You're Invited To {:invitation.name}</h3>
<a class='btn btn-success' data-type='guild' x-bind="click:acceptInvitation">Accept</a>
<a class='btn btn-danger' x-bind="click:rejectInvitation">Reject</a>
</div>
{/}
{/}
<app:groups:public-groups />
</div>
{{#each _guilds as :guild}}
<div class="tab-pane" id="groups-guild-{{:guild.id}}" >
<app:groups:group group={:guild} />
</div>
{{/}}
<div class='tab-pane' id='groups-guild-create'>
<app:groups:create-group type='guild' />
</div>
</div>
</div>
</div>
<public-groups:>
<table class="table table-striped">
{#each _publicGroups as :public}
<tr><td>
<ul class="pull-right challenge-accordion-header-specs">
<li>{count(:public.members)} member(s)</li>
<li>
<!-- join / leave -->
{#if indexOf(:public.members,_user.id)}
<a x-bind="click:groupLeave" data-id={{:public.id}} class='btn btn-small btn-danger'><i class='icon-ban-circle'></i> Leave</a>
{else}
<a x-bind="click:joinGroup" class='btn btn-small btn-success'><i class='icon-ok'></i> Join</a>
{/}
</li>
</ul>
<h4>{:public.name}</h4>
<p>{:public.description}</p>
</td></tr>
{/}
</table>
<create-group:>
<form class=form-horizontal x-bind="submit:groupCreate" data-type={{@type}} >
{#if _groupError}
<div class='alert alert-danger'>{_groupError}</div>
{/}
<div class="control-group whatever-options">
<div class=control-group>
<label class="control-label" for="new-group-name">{{#if equal(@type,'party')}}Party{{else}}Guild{{/}} Name</label>
<div class="controls">
<input required id=new-group-name type=text class="input-medium option-content" placeholder="{{#if equal(@type,'party')}}Party{{else}}Guild{{/}} Name" value="{_new.group.name}" />
</div>
</div>
<div class=control-group>
<label class="control-label" for="new-group-description">Description</label>
<div class="controls">
<textarea id=new-group-description cols=3 class='option-content' placeholder='Description'>{_new.group.description}</textarea>
</div>
</div>
{{#if equal(@type,'guild')}}
<div class=control-group>
<div class=controls>
<label class="radio">
<input type='radio' name='new-group-privacy' checked="{equal('public',_new.group.privacy)}" > Public
</label>
<label class="radio">
<input type='radio' name='new-group-privacy' checked="{equal('private',_new.group.privacy)}" > Invite Only
</label>
<input type="submit" class="btn {#unless and(_new.group.privacy,_new.group.name)}disabled{/}" value="Create" /><span class='gem-cost'>4 Gems</span>
<p><small>The Gem cost promotes high quality guilds, and is transferred into your guild's bank for use as rewards in the upcoming Challenges feature!</small></p>
</div>
</div>
{{else}}
<div class=control-group>
<div class=controls>
<input type="submit" class="btn " value="Create" />
</div>
</div>
{{/}}
</div>
</form>
<group:>
{{#if and(equal(@group.type,'guild'),not(equal(@group.id,'habitrpg')))}}
<a class="pull-right gem-wallet" rel="popover" data-trigger="hover" data-title="Guild Bank" data-content="Gems which your Guild leader can use for prizes in the upcoming <a target=_blank href='https://trello.com/card/challenges-individual-party-guild-public/50e5d3684fe3a7266b0036d6/58'>Challenges</a> feature." data-placement="bottom" data-html=true>
<!--<span class="task-action-btn tile flush bright add-gems-btn"></span>-->
<span class="task-action-btn tile flush neutral"><div class="Gems"></div>{{gems(@group.balance)}} Guild Gems</span>
</a>
{{/}}
<div class='row-fluid'>
<div class='span4'>
{{#if equal(@group.id,'habitrpg')}}
<div class='tavern-pane'>
<table><tr>
<td><div class='NPC-Daniel'></div></td>
<td>
<div class="popover static-popover fade right in">
<div class="arrow"></div>
<h3 class="popover-title">Daniel Johansson</h3>
<div class="popover-content">
Welcome to the Tavern! I'm <a target="_blank" href="http://www.kickstarter.com/profile/2014640723">Daniel</a>, the bar keep. If you want to rest a while (going on vacation? sudden illness?), I'll set you up at the inn - dailies won't hurt you while you're resting. Stay a while & meet the locals.
<div><button x-bind="click:toggleResting" class='btn btn-large btn-success {#if _user.flags.rest}active{/}'>{#if _user.flags.rest}Check Out of Inn{else}Rest In The Inn{/}</button></div>
</div>
</div>
</td>
</tr></table>
</div>
<div class='alert alert-info {#unless _user.flags.rest}hidden{/}'>Whilst resting, your dailies are saved and aren't affected by day turn-over. Whether you check out tomorrow or in a week's time, you'll continue in the same state as when you checked in.</div>
<div class=well>
<h3>Resources</h3>
<ul class=unstyled>
<li><h4><a target="_blank" href="http://community.habitrpg.com/forums/lfg">LFG Posts</a></h4></li>
<li><h4><a target="_blank" href="http://www.youtube.com/watch?feature=player_embedded&v=cT5ghzZFfao">Tutorial</a></h4></li>
<li><h4><a target="_blank" href="http://community.habitrpg.com/faq-page">FAQ</a></h4></li>
<li><h4><a target="_blank" href="http://community.habitrpg.com/node/280">Report a Problem</a></h4></li>
<li><h4><a target="_blank" href="https://trello.com/board/habitrpg/50e5d3684fe3a7266b0036d6">Request a Feature</a></h4></li>
<li><h4><a target="_blank" href="http://community.habitrpg.com/forum">Community Forum</a></h4></li>
</ul>
</div>
{{else}}
<h3>{@group.name}</h3>
<div class="accordion" id="accordion-{{@group.id}}-parent">
<div class="accordion-group">
<div class="accordion-heading">
<a class="accordion-toggle" data-toggle="collapse" data-parent="#accordion-{{@group.id}}-parent" href="#accordion-{{@group.id}}-information">Information</a>
</div>
<div id="accordion-{{@group.id}}-information" class="accordion-body collapse in">
<div class="accordion-inner blah-options">
{#if _editing.groups[@group.id]}
<div style="height:10px">
<a class=pull-right x-bind=click:toggleGroupEdit data-gid={{@group.id}} ><i class=icon-ok></i></a>
</div>
<input type=text value={@group.name} class='option-content' placeholder='Group Name' />
<textarea cols=3 placeholder='Description'>{@group.description}</textarea>
<input type=url class=option-content placeholder="Logo Url" value={@group.logo} />
{#with @group}
<form class='form-inline' x-bind="submit:groupAddWebsite" >
<input type=url placeholder='Website' class='option-content' value={_newGroupWebsite} />
<input type=submit value="Add" />
</form>
<h4>Assign Group Leader</h4>
<select id=group-leader-selection>
{#each @group.members as :memberId}
<option selected="{equal(:memberId,_new.groupLeader)}">{{username(_members[:memberId].auth,_members[:memberId].profile.name)}}</option>
{/}
</select>
<button x-bind=click:assignGroupLeader >Assign</button>
{/}
{#if @group.websites}
<h4>Resources</h4>
<ul class=unstyled>
{#each @group.websites as :website}
<li><a x-bind='click:removeAt'><i class='icon-trash'></i></a> <a target="_blank" href="{:website}" >{:website}</a></li>
{/}
</ul>
{/}
{else}
{#if @group.logo}<img class=pull-right style='max-width:150px' src={@group.logo} />{/}
{{#if equal(@group.leader,_user.id)}}
<a class=pull-right x-bind=click:toggleGroupEdit data-gid={{@group.id}} ><i class=icon-pencil></i></a>
{{/}}
<div>{@group.description}</div>
{#if @group.websites}
<h4>Resources</h4>
<ul class=unstyled>
{#each @group.websites as :website}
<li><a target="_blank" href="{:website}">{:website}</a></li>
{/}
</ul>
{/}
{/}
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-heading">
<a class="accordion-toggle" data-toggle="collapse" data-parent="#accordion-{{@group.id}}-parent" href="#accordion-{{@group.id}}-members">Members</a>
</div>
<div id="accordion-{{@group.id}}-members" class="accordion-body collapse">
<div class="accordion-inner">
<table class="table table-striped">
{#each @group.members as :memberId}
<tr><td>
<!-- allow leaders to ban members -->
{{#if and(equal(@group.leader,_user.id),not(equal(_user.id,:memberId)))}}
{{#with @group.members[$index]}}
<a x-bind=click:removeAt data-refresh=true data-confirm='Boot this member?'>
<i class=icon-ban-circle rel=tooltip title="Boot Member"></i>
</a>
{{/}}
&nbsp;
{{/}}
<a data-toggle='modal' data-target="#avatar-modal-{{:memberId}}">
<span class="{{#if equal(@group.leader,:memberId)}}badge badge-info{{/}}">
{{username(_members[:memberId].auth, _members[:memberId].profile.name)}}
</span>
</a>
</td>
<td>
({{:memberId}})
</td>
</tr>
{/}
</table>
{#with @group as :group}
<form class="form-inline" x-bind="submit:groupInvite" data-type="{@group.type}" >
{#if _groupError}
<div class='alert alert-danger'>{_groupError}</div>
{/}
<div class='control-group'>
<input type="text" class="input-medium" placeholder="User Id" value="{_groupInvitee}">
<input type="submit" class="btn" value="Invite" />
</div>
</form>
{/}
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-heading">
<a class="accordion-toggle" data-toggle="collapse" data-parent="#accordion-{{@group.id}}-parent" href="#accordion-{{@group.id}}-challenges">Challenges</a>
</div>
<div id="accordion-{{@group.id}}-challenges" class="accordion-body collapse">
<div class="accordion-inner">
<span class=label><i class=icon-bullhorn></i> Challenges</span> coming soon! <a target="_blank" href="https://trello.com/card/challenges-individual-party-guild-public/50e5d3684fe3a7266b0036d6/58">Details</a>
<!--{#if @group.challenges}
<table class="table table-striped">
{#each @group.challenges as :challenge}
<tr><td>
{:challenge.name}
</td></tr>
{/}
</table>
Visit the <span class=label><i class=icon-bullhorn></i> Challenges</span> for more information.
{else}
No challenges yet, visit the <span class=label><i class=icon-bullhorn></i> Challenges</span> tab to create one.
{/}-->
</div>
</div>
</div>
</div>
<a class='btn btn-danger' data-id="{{@group.id}}" x-bind="click:groupLeave">Leave</a>
{{/}}
</div>
<div class='span8'>
{{#if equal(@group.id,'habitrpg')}}
<h3>Tavern Talk & LFG</h3>
<div class='row-fluid'>
<div class='span3'>
<ul class='unstyled buttonList'>
<li><a class='btn btn-info' style='width:100%' target="_blank" href="http://community.habitrpg.com/faq-page">FAQ</a></li>
<li><a class='btn btn-info' style='width:100%' target="_blank" href="http://community.habitrpg.com/node/280">Report a Problem</a></li>
<li><a class='btn btn-info' style='width:100%' target="_blank" href="https://trello.com/board/habitrpg/50e5d3684fe3a7266b0036d6">Request a Feature</a></li>
</ul>
</div>
<div class=span9>
<app:groups:chat-box group={@group} />
</div>
</div>
{{else}}
{{#if equal(@group.leader,_user.id)}}
{#if _editing.leaderMessage[@group.id]}
<a x-bind=click:toggleLeaderMessageEdit data-gid={{@group.id}} class=pull-right><i class=icon-ok></i></a>
<textarea cols=3 placeholder='Message from group leader'>{@group.leaderMessage}</textarea>
{else}
<a x-bind=click:toggleLeaderMessageEdit data-gid={{@group.id}} class='btn pull-right'>Edit leader message</a>
{/}
{{/}}
{#if @group.leaderMessage}
<table><tr>
<td><app:avatar:avatar profile="{{_members[@group.leader]}}" /></td>
<td>
<div class="popover static-popover fade right in">
<div class="arrow"></div>
<h3 class="popover-title">{{username(_members[@group.leader].auth,_members[@group.leader].profile.name)}}</h3>
<div class="popover-content">{@group.leaderMessage}</div>
</div>
</td>
</tr></table>
{/}
<h3>Chat</h3>
<app:groups:chat-box group={@group} />
{{/}}
<ul class='unstyled tavern-chat'>
{#each @group.chat as :message}
<app:groups:chat-message message={{:message}} />
{/}
</ul>
</div>
</div>
<chat-box:>
{{#with @group as :group}}
<form x-bind='submit:sendChat'>
<textarea class="span6" rows="3" x-bind='keyup:chatKeyup'>{_chatMessage}</textarea><br/>
<input class=btn type=submit value="Send Chat" />
</form>
{{/}}
<chat-message:>
<li class="{{#if indexOf(:message.text, username(_user.auth, _user.profile.name))}}highlight{{/if}}">
<span
class="label {{#if @message.npc}}label-success{{else if @message.contributor}}label-inverse{{else if equal(@message.uuid,_user.id)}}label-info{{/}} chat-message"
rel='tooltip' title="{{@message.contributor}}{{@message.npc}}">
{{@message.user}}</span> {{@message.text}} - <span class='muted time'>{relativeDate(@message.timestamp, _currentTime)}
{{#if or(_user.backer.admin,equal(@message.uuid,_user.id))}}{{#with @message}}<a x-bind="click:deleteChatMessage"><i rel=tooltip title=Delete class=icon-remove></i></a>{{/}}{{/}}
</span>
</li>
+4
View File
@@ -0,0 +1,4 @@
form(x-bind='submit:sendChat')
textarea.span6(rows='3', x-bind='keyup:chatKeyup') {{_chatMessage}}
br
input.btn(type='submit', value='Send Chat')
+8
View File
@@ -0,0 +1,8 @@
li(ng-repeat='message in group.chat', ng-class='{highlight: indexOf(message.text,username(user.auth,user.profile.name))}')
span.label.chat-message(ng-class='{"label-success": message.npc, "label-inverse": message.contributor, "label-info": message.uuid == user.id}', tooltip='{{message.contributor}}{{message.npc}}')
| {{message.user}}
| {{message.text}} -
span.muted.time
| {{relativeDate(message.timestamp, _currentTime)}}
a(ng-show='user.backer.admin || message.uuid == user.id', x-bind='click:deleteChatMessage')
i.icon-remove(tooltip='Delete')
+27
View File
@@ -0,0 +1,27 @@
form.form-horizontal(x-bind='submit:groupCreate', data-type='{{type}}')
.alert.alert-danger(ng-show='_groupError') {{_groupError}}
.control-group.whatever-options
.control-group
label.control-label(for='new-group-name') {{text}} Name
.controls
input#new-group-name.input-medium.option-content(required, type='text', placeholder='{{text}} Name', value='{{_new.group.name}}')
.control-group
label.control-label(for='new-group-description') Description
.controls
textarea#new-group-description.option-content(cols='3', placeholder='Description') {{_new.group.description}}
.control-group(ng-show='type=="guild"')
.controls
label.radio
input(type='radio', name='new-group-privacy', checked='{{"public"==_new.group.privacy}}')
| Public
label.radio
input(type='radio', name='new-group-privacy', checked='{{"private"==_new.group.privacy}}')
| Invite Only
input.btn(type='submit', ng-disabled='!_new.group.privacy && !_new.group.name', value='Create')
span.gem-cost 4 Gems
p
small
| The Gem cost promotes high quality guilds, and is transferred into your guild's bank for use as rewards in the upcoming Challenges feature!
.control-group(ng-show='type=="party"')
.controls
input.btn(type='submit', value='Create')
+108
View File
@@ -0,0 +1,108 @@
a.pull-right.gem-wallet(ng-show='group.type=="guild" && group.id!="habitrpg"', rel='popover', data-trigger='hover', data-title='Guild Bank', data-content='Gems which your Guild leader can use for prizes in the upcoming <a target=_blank href="https://trello.com/card/challenges-individual-party-guild-public/50e5d3684fe3a7266b0036d6/58">Challenges</a> feature.', data-placement='bottom', data-html='true')
// <span class="task-action-btn tile flush bright add-gems-btn"></span>
span.task-action-btn.tile.flush.neutral
.Gems
| {{gems(group.balance)}} Guild Gems
.row-fluid
.span4
h3 {{group.name}}
accordion
accordion-group(heading='Information')
.blah-options
div(ng-show='_editing.groups[group.id]')
div(style='height:10px')
a.pull-right(x-bind='click:toggleGroupEdit', data-gid='{{group.id}}')
i.icon-ok
input.option-content(type='text', value='{group.name}', placeholder='Group Name')
textarea(cols='3', placeholder='Description') {{group.description}}
input.option-content(type='url', placeholder='Logo Url', value='{group.logo}')
form.form-inline(x-bind='submit:groupAddWebsite')
input.option-content(type='url', placeholder='Website', value='{_newGroupWebsite}')
input(type='submit', value='Add')
h4 Assign Group Leader
select#group-leader-selection
option(ng-repeat='memberId in group.members', selected='{equal(memberId,_new.groupLeader)}') {{username(_members[memberId].auth,_members[memberId].profile.name)}}
button(x-bind='click:assignGroupLeader') Assign
div(ng-show='group.websites')
h4 Resources
ul.unstyled
li
a(ng-repeat='website in group.websites', x-bind='click:removeAt')
i.icon-trash
a(target='_blank', href='{website}') {{website}}
div(ng-show='!_editing.groups[group.id]')
img.pull-right(ng-show='group.logo', style='max-width:150px', src='{group.logo}')
a.pull-right(ng-if='group.leader==user.id', x-bind='click:toggleGroupEdit', data-gid='{{group.id}}')
i.icon-pencil
div {{group.description}}
div(ng-show='group.websites')
h4 Resources
ul.unstyled
li(ng-repeat='website in group.websites')
a(target='_blank', ng-href='{{website}}') {{website}}
accordion-group(heading='Members')
table.table.table-striped
tr(ng-repeat='memberId in group.members')
td
// allow leaders to ban members
div(ng-show='and(equal(group.leader,_user.id),not(equal(_user.id,memberId)))')
// {{#with group.members[$index]}}
a(x-bind='click:removeAt', data-refresh='true', data-confirm='Boot this member?')
i.icon-ban-circle(rel='tooltip', title='Boot Member')
// {{/}}
a(data-toggle='modal', data-target='#avatar-modal-{{memberId}}')
span(ng-class='{"badge badge-info": group.leader==memberId}')
| {{username(_members[memberId].auth, _members[memberId].profile.name)}}
td
| ({{memberId}})
// {#with group as :group}
form.form-inline(x-bind='submit:groupInvite', data-type='{group.type}')
.alert.alert-danger(ng-show='_groupError') {_groupError}
.control-group
input.input-medium(type='text', placeholder='User Id', value='{_groupInvitee}')
input.btn(type='submit', value='Invite')
// {/}
//-accordion-group(heading='Challenges')
span.label
i.icon-bullhorn
| Challenges
| coming soon!
a(target='_blank', href='https://trello.com/card/challenges-individual-party-guild-public/50e5d3684fe3a7266b0036d6/58') Details
//-{#if group.challenges}
//- <table class="table table-striped">
//- {#each group.challenges as :challenge}
//- <tr><td>
//- {:challenge.name}
//- </td></tr>
//- {/}
//- </table>
//- Visit the <span class=label><i class=icon-bullhorn></i> Challenges</span> for more information.
//-{else}
//- No challenges yet, visit the <span class=label><i class=icon-bullhorn></i> Challenges</span> tab to create one.
//-{/}
a.btn.btn-danger(data-id='{{group.id}}', x-bind='click:groupLeave') Leave
.span8
div(ng-if='group.leader==user.id')
div(ng-show='_editing.leaderMessage[group.id]')
a.pull-right(x-bind='click:toggleLeaderMessageEdit', data-gid='{{group.id}}')
i.icon-ok
textarea(cols='3', placeholder='Message from group leader') {group.leaderMessage}
div(ng-hide='_editing.leaderMessage[group.id]')
a.btn.pull-right(x-bind='click:toggleLeaderMessageEdit', data-gid='{{group.id}}') Edit leader message
table(ng-show='group.leaderMessage')
tr
td
app:avatar:avatar(profile='{{_members[group.leader]}}')
td
.popover.static-popover.fade.right.in
.arrow
h3.popover-title {{username(_members[group.leader].auth,_members[group.leader].profile.name)}}
.popover-content {group.leaderMessage}
h3 Chat
include ./chat-box
ul.unstyled.tavern-chat
include ./chat-message
+52
View File
@@ -0,0 +1,52 @@
div(ng-controller='GroupsCtrl')
h4.alert.alert-warning Coming Soon! (Presently Incomplete & Read-Only)
tabset
tab(heading='Party')
div(ng-show='groups.party.id', ng-controller='PartyCtrl')
app:groups:group(group='{groups[_party.id]}')
div(ng-hide='groups.party.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 {{party.name}}
a.btn.btn-success(data-type='party', x-bind='click:acceptInvitation') Accept
a.btn.btn-danger(x-bind='click:rejectInvitation') 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(heading='Guilds')
tabset
tab(heading='Public Guilds')
div(ng-repeat='invitation in user.invitations.guilds')
h3 You're Invited To {{invitation.name}}
a.btn.btn-success(data-type='guild', x-bind='click:acceptInvitation') Accept
a.btn.btn-danger(x-bind='click:rejectInvitation') Reject
// Public Groups
table.table.table-striped
tr(ng-repeat='group in groups.public')
td
ul.pull-right.challenge-accordion-header-specs
li {{count(group.members)}} member(s)
li
// join / leave
a.btn.btn-small.btn-danger(ng-show='indexOf(group.members,user.id)', x-bind='click:groupLeave', data-id='{{group.id}}')
i.icon-ban-circle
| Leave
a.btn.btn-small.btn-success(ng-hide='indexOf(group.members,user.id)', x-bind='click:joinGroup')
i.icon-ok
| Join
h4 {{group.name}}
p {{group.description}}
tab(ng-repeat='group in groups.guilds', heading='{{group.name}}')
include ./group
tab(heading='Create Guild')
div(ng-controller='GuildCtrl')
include ./group
+58
View File
@@ -0,0 +1,58 @@
h4.alert.alert-warning Coming Soon! (Presently Incomplete & Read-Only)
.row-fluid(ng-controller='TavernCtrl')
.span4
.tavern-pane
table
tr
td
.NPC-Daniel
td
.popover.static-popover.fade.right.in
.arrow
h3.popover-title Daniel Johansson
.popover-content
| Welcome to the Tavern! I'm
a(target='_blank', href='http://www.kickstarter.com/profile/2014640723') Daniel
| , the bar keep. If you want to rest a while (going on vacation? sudden illness?), I'll set you up at the inn - dailies won't hurt you while you're resting. Stay a while & meet the locals.
div
button.btn.btn-large.btn-success(ng-class='{active: user.flags.rest}',x-bind='click:toggleResting')
span(ng-show='user.flags.rest') Check Out of Inn
span(ng-hide='user.flags.rest') Rest In The Inn
.alert.alert-info(ng-hide='user.flags.rest')
| Whilst resting, your dailies are saved and aren't affected by day turn-over. Whether you check out tomorrow or in a week's time, you'll continue in the same state as when you checked in.
.well
h3 Resources
ul.unstyled
li
h4
a(target='_blank', href='http://community.habitrpg.com/forums/lfg') LFG Posts
li
h4
a(target='_blank', href='http://www.youtube.com/watch?feature=player_embedded&v=cT5ghzZFfao') Tutorial
li
h4
a(target='_blank', href='http://community.habitrpg.com/faq-page') FAQ
li
h4
a(target='_blank', href='http://community.habitrpg.com/node/280') Report a Problem
li
h4
a(target='_blank', href='https://trello.com/board/habitrpg/50e5d3684fe3a7266b0036d6') Request a Feature
li
h4
a(target='_blank', href='http://community.habitrpg.com/forum') Community Forum
.span8
h3 Tavern Talk & LFG
.row-fluid
.span3
ul.unstyled.buttonList
li
a.btn.btn-info(style='width:100%', target='_blank', href='http://community.habitrpg.com/faq-page') FAQ
li
a.btn.btn-info(style='width:100%', target='_blank', href='http://community.habitrpg.com/node/280') Report a Problem
li
a.btn.btn-info(style='width:100%', target='_blank', href='https://trello.com/board/habitrpg/50e5d3684fe3a7266b0036d6') Request a Feature
.span9
include ./chat-box
ul.unstyled.tavern-chat
include ./chat-message
+6 -6
View File
@@ -10,13 +10,13 @@
| Profile
include ./profile
//-tab
tab
tab-heading
i.icon-heart
| Groups
app:groups:groups-pane
include ./groups/index
tab(ng-show='user.flags.dropsEnabled')
//-tab(ng-show='user.flags.dropsEnabled')
tab-heading
i.icon-gift
| Inventory
@@ -28,17 +28,17 @@
h2 Market
app:game-pane:market
tab(ng-show='user.flags.dropsEnabled')
//-tab(ng-show='user.flags.dropsEnabled')
tab-heading
i.icon-leaf
| Stable
app:pets:stable
//-tab
tab
tab-heading
i.icon-eye-close
| Tavern
app:groups:group(group='{_habitRPG}')
include ./groups/tavern
tab
tab-heading