From 17a9469370720e1a76ad3a00c2d02837b6185c94 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Thu, 29 Aug 2013 20:18:39 -0400 Subject: [PATCH] rewrite: start implementing groups. Note angular-bootstrap 0.4.0 required for nested tabs --- assets/js/controllers/groupsCtrl.js | 42 ++- bower.json | 2 +- src/controllers/deprecated.js | 2 +- src/controllers/groups.js | 66 +++++ src/controllers/{api.js => user.js} | 0 src/routes/api.js | 65 +++-- views/options/groups.html | 363 ------------------------- views/options/groups/chat-box.jade | 4 + views/options/groups/chat-message.jade | 8 + views/options/groups/create-group.jade | 27 ++ views/options/groups/group.jade | 108 ++++++++ views/options/groups/index.jade | 52 ++++ views/options/groups/tavern.jade | 58 ++++ views/options/index.jade | 12 +- 14 files changed, 403 insertions(+), 406 deletions(-) create mode 100644 src/controllers/groups.js rename src/controllers/{api.js => user.js} (100%) delete mode 100644 views/options/groups.html create mode 100644 views/options/groups/chat-box.jade create mode 100644 views/options/groups/chat-message.jade create mode 100644 views/options/groups/create-group.jade create mode 100644 views/options/groups/group.jade create mode 100644 views/options/groups/index.jade create mode 100644 views/options/groups/tavern.jade diff --git a/assets/js/controllers/groupsCtrl.js b/assets/js/controllers/groupsCtrl.js index b609a4e9dc..32db899921 100644 --- a/assets/js/controllers/groupsCtrl.js +++ b/assets/js/controllers/groupsCtrl.js @@ -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; + }); + } + ]) \ No newline at end of file diff --git a/bower.json b/bower.json index 597df5bf04..ac33f2b547 100644 --- a/bower.json +++ b/bower.json @@ -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", diff --git a/src/controllers/deprecated.js b/src/controllers/deprecated.js index 5852e0a297..3ebbae8cac 100644 --- a/src/controllers/deprecated.js +++ b/src/controllers/deprecated.js @@ -8,7 +8,7 @@ _ = require('lodash'); icalendar = require('icalendar'); -api = require('./api'); +api = require('./user'); /* ---------- Deprecated Paths ------------*/ diff --git a/src/controllers/groups.js b/src/controllers/groups.js new file mode 100644 index 0000000000..8c4c75cb06 --- /dev/null +++ b/src/controllers/groups.js @@ -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); + }); +}; \ No newline at end of file diff --git a/src/controllers/api.js b/src/controllers/user.js similarity index 100% rename from src/controllers/api.js rename to src/controllers/user.js diff --git a/src/routes/api.js b/src/routes/api.js index 78f8de84c5..7a6616d622 100644 --- a/src/routes/api.js +++ b/src/routes/api.js @@ -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; \ No newline at end of file diff --git a/views/options/groups.html b/views/options/groups.html deleted file mode 100644 index a82a39e694..0000000000 --- a/views/options/groups.html +++ /dev/null @@ -1,363 +0,0 @@ - - - -
-
- {#if _party.id} - - {else if _user.invitations.party} - - {#with _user.invitations.party as :party} -

You're Invited To {:party.name}

- Accept - Reject - {/} - {else} -

Create A Party

-

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:

-
{_user.id}
- - {/} -
- -
- -
- -
- - {#if and(_user.invitations,_user.invitations.guilds)} - {#each _user.invitations.guilds as :invitation} -
-

You're Invited To {:invitation.name}

- Accept - Reject -
- {/} - {/} - -
- - {{#each _guilds as :guild}} -
- -
- {{/}} - -
- -
- -
-
-
- - - - {#each _publicGroups as :public} - - {/} -
-
    -
  • {count(:public.members)} member(s)
  • -
  • - - {#if indexOf(:public.members,_user.id)} - Leave - {else} - Join - {/} -
  • -
-

{:public.name}

-

{:public.description}

-
- - -
- {#if _groupError} -
{_groupError}
- {/} -
-
- -
- -
-
-
- -
- -
-
- - {{#if equal(@type,'guild')}} -
-
- - - 4 Gems -

The Gem cost promotes high quality guilds, and is transferred into your guild's bank for use as rewards in the upcoming Challenges feature!

-
-
- {{else}} -
-
- -
-
- {{/}} - -
-
- - - - {{#if and(equal(@group.type,'guild'),not(equal(@group.id,'habitrpg')))}} - - -
{{gems(@group.balance)}} Guild Gems
-
- {{/}} -
-
- {{#if equal(@group.id,'habitrpg')}} -
- - - -
-
-
-

Daniel Johansson

-
- Welcome to the Tavern! I'm 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. -
-
-
-
-
-
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.
- - - {{else}} -

{@group.name}

-
-
- -
-
- {#if _editing.groups[@group.id]} -
- -
- - - - {#with @group} -
- - -
-

Assign Group Leader

- - - - {/} - {#if @group.websites} -

Resources

-
    - {#each @group.websites as :website} -
  • {:website}
  • - {/} -
- {/} - {else} - {#if @group.logo}{/} - {{#if equal(@group.leader,_user.id)}} - - {{/}} -
{@group.description}
- {#if @group.websites} -

Resources

-
    - {#each @group.websites as :website} -
  • {:website}
  • - {/} -
- {/} - {/} -
-
-
- -
-
- Members -
-
-
- - {#each @group.members as :memberId} - - - - {/} -
- - {{#if and(equal(@group.leader,_user.id),not(equal(_user.id,:memberId)))}} - {{#with @group.members[$index]}} - - - - {{/}} -   - {{/}} - - - {{username(_members[:memberId].auth, _members[:memberId].profile.name)}} - - - - ({{:memberId}}) -
- {#with @group as :group} -
- {#if _groupError} -
{_groupError}
- {/} -
- - -
-
- {/} -
-
-
- -
- -
-
- Challenges coming soon! Details - -
-
-
- - -
- Leave - {{/}} - -
-
- - {{#if equal(@group.id,'habitrpg')}} -

Tavern Talk & LFG

-
- -
- -
-
- {{else}} - {{#if equal(@group.leader,_user.id)}} - {#if _editing.leaderMessage[@group.id]} - - - {else} - Edit leader message - {/} - {{/}} - {#if @group.leaderMessage} - - - -
-
-
-

{{username(_members[@group.leader].auth,_members[@group.leader].profile.name)}}

-
{@group.leaderMessage}
-
-
- {/} -

Chat

- - {{/}} - -
    - {#each @group.chat as :message} - - {/} -
-
-
- - - {{#with @group as :group}} -
-
- -
- {{/}} - - -
  • - - {{@message.user}} {{@message.text}} - {relativeDate(@message.timestamp, _currentTime)} - {{#if or(_user.backer.admin,equal(@message.uuid,_user.id))}}{{#with @message}}{{/}}{{/}} - -
  • diff --git a/views/options/groups/chat-box.jade b/views/options/groups/chat-box.jade new file mode 100644 index 0000000000..b10454fa7b --- /dev/null +++ b/views/options/groups/chat-box.jade @@ -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') diff --git a/views/options/groups/chat-message.jade b/views/options/groups/chat-message.jade new file mode 100644 index 0000000000..f585b279bc --- /dev/null +++ b/views/options/groups/chat-message.jade @@ -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') diff --git a/views/options/groups/create-group.jade b/views/options/groups/create-group.jade new file mode 100644 index 0000000000..e1f08e0b39 --- /dev/null +++ b/views/options/groups/create-group.jade @@ -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') diff --git a/views/options/groups/group.jade b/views/options/groups/group.jade new file mode 100644 index 0000000000..3cbc96164d --- /dev/null +++ b/views/options/groups/group.jade @@ -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 Challenges feature.', data-placement='bottom', data-html='true') + // + 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} + //- + //- {#each group.challenges as :challenge} + //- + //- {/} + //-
    + //- {:challenge.name} + //-
    + //- Visit the Challenges for more information. + //-{else} + //- No challenges yet, visit the Challenges 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 diff --git a/views/options/groups/index.jade b/views/options/groups/index.jade new file mode 100644 index 0000000000..07c04108d5 --- /dev/null +++ b/views/options/groups/index.jade @@ -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 diff --git a/views/options/groups/tavern.jade b/views/options/groups/tavern.jade new file mode 100644 index 0000000000..b04ee125b7 --- /dev/null +++ b/views/options/groups/tavern.jade @@ -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 diff --git a/views/options/index.jade b/views/options/index.jade index 48e2e0df86..30443ce2f1 100644 --- a/views/options/index.jade +++ b/views/options/index.jade @@ -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